home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MACD 5
/
MACD 5.bin
/
workbench
/
libs
/
intoids.lha
/
Intoids 1.0
/
Source
/
Intoids.c
next >
Wrap
Text File
|
1997-02-12
|
160KB
|
5,119 lines
/****** intoids.library/--background-- **************************************
*
* NAME
* intoids.library -- Arbitrary precision integer math in 32 bits.
*
* FUNCTION
* This set of subroutines lets other programs easily handle large
* integer numbers. It is a wrapper around the GNU Integer library
* (which implements large integers as an array of shorts) that lets you
* deal with all integers as 32 bit values (small integers are stored as
* a shifted odd value and larger ones are stored as a pointer to a
* variation of the GNU large integer). It also supports special values
* for infinity and not-a-number conditions. Written to support large
* sized files in the upcoming AGMS virtual file system.
*
* INPUTS
* RecycleMe - most functions that return an Intoid also take a
* parameter called RecycleMe. Treat it as if it is used in a call
* to the FreeIntoid function. If RecycleMe isn't NULL then its
* storage will be recycled to hold the result (or reallocated if it
* is too small). If RecycleMe is NULL then new memory will be
* allocated for the function result. If the function fails
* (returns NULL) then the RecycleMe variable is deallocated. This
* is here mostly to reduce the number of memory allocations that
* would otherwise be made.
*
* RESULT
* Intoid values - all functions returning Intoids will return NULL if
* out of memory or if the number is too big to be represented.
* Some also return NULL for not-a-number error conditions (like
* divide by zero).
*
* COPYRIGHT
* Modifications for storing smaller integers in 32 bit values and
* conversion to an Amiga library copyright (C) 1996 by Alexander G. M.
* Smith. Original long integer code copyright (C) 1988 Free Software
* Foundation.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* AUTHOR
* Modifications for the Amiga library and 32 bit storage implemented by
* Alexander G. M. Smith, Ottawa Canada, agmsmith@achilles.net,
* agmsmith@FreeNet.Carleton.ca, agmsmith@bix.com,
* 71330.3173@compuserve.com, and probably other places in the future.
* Send mail to all these addresses to find ones which are still valid
* (list made in November 1996).
*
* Original long integer code written by Doug Lea (dl@rocky.oswego.edu).
* The GNU G++ library's Integer.cc file also has these attributions:
* Some of the following algorithms are very loosely based on those from
* MIT C-Scheme bignum.c, which is
* Copyright (c) 1987 Massachusetts Institute of Technology
* with other guidance from Knuth, vol. 2
* Thanks to the creators of the algorithms.
*
* NOTES
* To compile intoids.library, use the SAS C compiler, version 6.56.
* Use 32 bit integers, 32 bit longs and 16 bit shorts. Naturally, you
* can use whatever compiler you want to write code for calling the
* library (SASC and GNU are supported, and there is an .FD file for
* making interfaces for other compilers). Here's the SCOPTIONS file I
* used for making intoids.library:
* PARAMETERS=REGISTERS
* NOSTACKCHECK
* NOCHECKABORT
* ERRORREXX
* OPTIMIZE
* LINK
* LISTMACROS
* LISTINCLUDES
* STRINGSCONST
* OPTIMIZERINLINELOCAL
* VERBOSE
* MAP
* MAPHUNK
* MAPSYMBOLS
* MAPLIB
* MAPXREFERENCE
* STRICT
* LIBRARYCODE
* OPTIMIZERTIME
* STRINGSECTION=CODE
* STARTUP=libinit
* PROGRAMNAME=intoids.library
* MAPFILE=Intoids.map
* PUBSCREEN=Workbench
* LIBRARYFDFILE=Intoids.fd
* LIBRARYVERSION=1
* LIBRARYREVISION=0
* OPTIMIZERCOMPLEXITY=1
* OPTIMIZERDEPTH=1
* OPTIMIZERRECURDEPTH=0
*
* BUGS
* Not all the functions from the GNU Integer library were implemented.
* If you want modulo, power and other functions, either ask me or do it
* yourself. It's a matter of cutting and pasting the code from GNU's
* Integer.cc and fixing up a few things to handle Intoids rather than
* IntReps.
*
* SEE ALSO
* GNU's G++ library Integer.cc file for the original code and yet more
* arbitrary precision functions.
*
*****************************************************************************
* $Header: Big:Programming/C/Intoids/Library/RCS/Intoids.c,v 1.28 1997/02/12 17:35:04 AGMS Exp $
*
* $Log: Intoids.c,v $
* Revision 1.28 1997/02/12 17:35:04 AGMS
* Enable custom version string.
*
* Revision 1.27 1997/02/12 17:31:43 AGMS
* Changed some titles for better autodoc index.
*
* Revision 1.26 1997/02/12 16:45:08 AGMS
* Added AGMS Portable Integer Format stuff.
*
* Revision 1.25 1997/01/21 17:50:25 AGMS
* Added autodocs for new AGMS Portable Integer functions.
*
* Revision 1.24 1997/01/14 17:12:42 AGMS
* Added AutoDoc comments for all the exported functions. Realised
* that there are no portable binary number formats...
*
* Revision 1.23 1997/01/14 12:39:26 AGMS
* Started adding AutoDoc comments.
*
* Revision 1.22 1997/01/12 17:57:15 AGMS
* Fixed a memory leak with multiplying two small integers with a
* result that didn't fit in a long. Also added more parameter
* error checking.
*
* Revision 1.21 1997/01/12 12:31:53 AGMS
* Changed to fit in with the standard Amiga C compiler include
* directory structure. Also uses standard Amiga type names
* (STRPTR instead of char *). Unfortunately that loses the
* const declarations for some of the arguments.
*
* Revision 1.20 1996/12/29 09:27:58 AGMS
* *** empty log message ***
*
* Revision 1.19 1996/12/29 09:05:02 AGMS
* Dropped in division functions from the GNU library.
*
* Revision 1.18 1996/12/28 13:36:21 AGMS
* Added remaining multiplication code and some utility functions.
*
* Revision 1.17 1996/12/18 16:40:48 AGMS
* Added functions for negating and comparing Intoids, fixed some bugs.
*
* Revision 1.16 1996/12/12 19:26:36 AGMS
* Found getreg function, can call the utility.library long division
* routines directly and get both the division result and the remainder.
*
* Revision 1.15 1996/12/12 18:22:48 AGMS
* Adding a long to an IntRep and Multiplying an IntRep by a
* long now work.
*
* Revision 1.14 1996/12/09 16:55:34 AGMS
* Now use SHORT_PER_LONG and do other things to make it work
* even for longs that aren't 32 bits (but probably some things
* still aren't ready for 64 bit longs). Multiplication also
* under construction.
*
* Revision 1.13 1996/12/08 17:22:23 AGMS
* Addition seems to work, now for multiplication!
*
* Revision 1.12 1996/12/08 15:22:15 AGMS
* Addition functions typed in, ready for testing.
*
* Revision 1.11 1996/12/07 17:06:43 AGMS
* Converted to use less indirection.
*
* Revision 1.10 1996/12/07 14:17:21 AGMS
* Oops, just realised that you don't need the extra level of
* indirection in Intoids, can point at the IntRep directly,
* not at a pointer to the IntRep.
*
* Revision 1.9 1996/12/04 17:01:31 AGMS
* Changed some inline functions to be macros for better optimization.
* ResizeIntoid now leaves old value alone if requested.
* Added functions for normalizing and copying Intoids.
* Addition function under construction.
*
* Revision 1.8 1996/12/03 16:17:42 AGMS
* Now with even more international support. Uses Utility.library for
* 32 bit math (division with remainder mostly).
*
* Revision 1.7 1996/11/28 16:04:41 AGMS
* Typed in stuff needed for printing an Intoid.
*
* Revision 1.6 1996/11/23 17:22:49 AGMS
* Now compiles and operates as a library, still no guts.
*
* Revision 1.5 1996/11/21 16:37:30 AGMS
* Not much.
*
* Revision 1.4 1996/11/21 16:16:00 AGMS
* Now compiles, but doesn't have any guts yet.
*
* Revision 1.3 1996/11/18 17:25:09 AGMS
* Documentation changes.
*
* Revision 1.2 1996/11/14 18:00:40 AGMS
* Added GNU license header info.
*
* Revision 1.1 1996/11/14 15:50:03 AGMS
* Initial revision
*/
#define __USE_SYSBASE 1
/* Need this to make the exec.library headers use the library base global
variable SysBase in SAS C (otherwise it just uses location $4 which slows
things down because it is in CHIP memory). */
#include <limits.h> /* For CHAR_BIT definition. */
#include <string.h> /* For string reverse function. */
#include <dos.h> /* For getreg builtin function. */
#include <proto/exec.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <proto/intuition.h>
#include <proto/locale.h>
#include <proto/utility.h>
#include <utility/utility.h>
#include <libraries/Intoids.h> /* Get datatypes, no function prototypes. */
#if __SASC
/* Special register assignment keywords (parameters passed in registers,
not on the stack) for making the library, SAS/C Amiga dialect. */
#define REGA0 register __a0
#define REGA1 register __a1
#define REGA6 register __a6
#define REGD0 register __d0
#define REGD1 register __d1
/* Declare the function as requiring a reload of the global data
pointer and as using register arguments. */
#define LIBFUNC __saveds __asm
#else /* Some other compiler. */
#define REGA0
#define REGA1
#define REGA6
#define REGD0
#define REGD1
#define LIBFUNC
#endif
/* Because the debugger doesn't recognize static functions in shared
libraries, disable the "static" function declaration when doing
debugging. Of course, it doesn't really matter since most code
can link with the shared library code only through the exported
interface (which uses only functions identified as LIBFUNC).
The exception is the library startup code. */
#if _DEBUG
#define LOCALFUNC
#else /* Normal use. */
#define LOCALFUNC static
#endif
/* AutoRequestor values for the body text, similar to AUTOFRONTPEN and
other default values. */
#define BODYLEFTEDGE 6
#define BODYTOPEDGE 5
/*
Sizes of shifts for multiple-precision arithmetic.
These should not be changed unless Integer representation
as unsigned shorts is changed in the implementation files.
Also, some other stuff has been hard coded to use shorts,
like the macros that strip off the I_SHIFT bits from a
long integer.
*/
#define I_SHIFT (sizeof(short) * CHAR_BIT)
#define I_RADIX ((unsigned long)(1L << I_SHIFT))
#define I_MAXNUM ((unsigned long)((I_RADIX - 1)))
#define I_MINNUM ((unsigned long)(I_RADIX >> 1))
#define I_POSITIVE 1
#define I_NEGATIVE 0
/* All routines assume SHORT_PER_LONG > 1 */
#define SHORT_PER_LONG ((unsigned)(((sizeof(long) + sizeof(short) - 1) / sizeof(short))))
#define CHAR_PER_LONG ((unsigned)sizeof(long))
/*
minimum and maximum sizes for an IntRep (number of shorts in the IntRep
number storage array).
*/
#define MINIntRep_SIZE 4
#define MAXIntRep_SIZE I_MAXNUM
typedef struct IntRepStruct
{
unsigned short currentLength; /* Number of entries in numberArray. */
BOOL positiveSign; /* 1 means >= 0; 0 means < 0. */
unsigned short allocatedLength; /* 0 means constant. Number of shorts. */
unsigned short numberArray [1]; /* least significant value in [0]. */
} IntRep, *IntRepPointer;
/* Large integer representation, essentially the same as in the GNU G++
Integer library. The numberArray (and thus the total record size) grows as
needed. Unused entries in numberArray always contain zero (an algorithm
invariant), so that it is easier to grow into them. The allocatedLength is
the number of shorts in numberArray. Zero allocatedLength means that the
number is a special constant value that should not be freed (allocated as a
global variable, not dynamically allocated). This special constant feature
isn't used because Intoids can represent special small integers (like 0 and
+-1) efficiently. */
/* Copied from Intoids.h for easier reference: */
/* typedef void * Intoid; */
/* As far as the user sees it, it is an opaque type. Treat it like
a pointer to a dynamically allocated object, though it sometimes
isn't and sometimes is. A NULL (zero) value Intoid means that an error
happened (out of memory, divide by zero, etc) [...] Besides storing really
big numbers and not-a-number, the Intoids can also represent positive and
negative infinity. */
typedef IntRepPointer IntoidAsPointer;
typedef signed long IntoidAsLong;
/* Intoid encoding: values that are a multiple of 4 are a pointer to an
IntRep. Odd values are a small integer value shifted left by two, with the
low bit set (arithmatic shift right by 1 bit to get the integer). Other
special codes are drawn from values generated by 2 + 4 * n: positive
infinity as 6, negative infinity as 10, see IntoidSpecialCodes for more
details. */
typedef enum IntoidSpecialCodesEnum
{
ISC_NOT_A_NUMBER = 0,
ISC_POSITIVE_INFINITY,
ISC_NEGATIVE_INFINITY,
ISC_MAX
} IntoidSpecialCodes;
/* The various special codes, used for non-integer numbers like infinity or
not-a-number (which is used here as the NULL pointer of numbers). In the
actual 32 bit Intoid, the value used is 2 + 4 * IntoidSpecialCode. The
exception is the Intoid of all zero which also corresponds to
ISC_NOT_A_NUMBER too. Precomputed for INTOID_POSITIVE_INFINITY and
INTOID_NEGATIVE_INFINITY. */
/******************************************************************************
* G L O B A L C O N S T A N T D A T A
*
* This stuff gets allocated in the code segment (static const declarations).
* Since this is a library, it would be wastefully in both code and data
* segments if we asked for the data segment (all hunks get loaded then the
* data ones get copied to the separately allocated library base record).
*/
#if 1 /* bleeble */
const char __far _LibID [] = "intoids 1.0 " __AMIGADATE__
" (by AGMSmith) ($Id: Intoids.c,v 1.28 1997/02/12 17:35:04 AGMS Exp $)\r\n";
/* Library version string used in the ROMTag, similar to the VER: full
version string (if the ROMTag exists, it's string is used by the AmigaDOS
version command instead of the VER: string). Overrides a string defined by
the linker, which just says "Version 1.0", this causes a link time error
(but it still links anyways). */
#endif
static const char CreditsMessage [] = /* static const for code segment. */
"$VER: intoids 1.0 " __AMIGADATE__ " ($Id: Intoids.c,v 1.28 1997/02/12 17:35:04 AGMS Exp $)\n"
"\n"
"Intoids.library - An Amiga runtime shared code library for efficiently\n"
"handling large and small integer values using pointer sized data fields.\n"
"\n"
"Modifications for storing smaller integers in 32 bit values and conversion\n"
"to an Amiga library copyright © 1996 by Alexander G. M. Smith.\n"
"Original long integer code copyright © 1988 Free Software Foundation.\n"
"\n"
"This library is free software; you can redistribute it and/or\n"
"modify it under the terms of the GNU Library General Public\n"
"License as published by the Free Software Foundation; either\n"
"version 2 of the License, or (at your option) any later version.\n"
"\n"
"This library is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
"Library General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU Library General Public\n"
"License along with this library; if not, write to the Free\n"
"Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
"\n"
"Modifications for the Amiga library and 32 bit storage implemented by\n"
"Alexander G. M. Smith, Ottawa Canada, agmsmith@achilles.net,\n"
"agmsmith@FreeNet.Carleton.ca, agmsmith@bix.com, 71330.3173@compuserve.com,\n"
"and probably other places in the future. Send mail to all these addresses\n"
"to find ones which are still valid (list made in November 1996).\n"
"\n"
"Original long integer code written by Doug Lea (dl@rocky.oswego.edu).\n"
"The GNU G++ library's Integer.cc file also has these attributions:\n"
" Some of the following algorithms are very loosely based on those from\n"
" MIT C-Scheme bignum.c, which is\n"
" Copyright (c) 1987 Massachusetts Institute of Technology\n"
" with other guidance from Knuth, vol. 2\n"
" Thanks to the creators of the algorithms.\n";
static const char * const DefaultErrorStrings [MSG_INTOIDS_MAX] =
{
/* MSG_INTOIDS_NOT_A_NUMBER */ "not-a-number",
/* MSG_INTOIDS_INFINITY */ "infinity",
/* MSG_INTOIDS_NUMBER_TOO_BIG_TO_PRINT */ "(too big to print)",
/* MSG_INTOIDS_PRINTING_OUT_OF_MEMORY */ "(out of memory)",
/* MSG_INTOIDS_NEW_TOO_BIG */ "Attempt to create a number bigger than the maximum allowed size.",
/* MSG_INTOIDS_INTREP_OUT_OF_MEMORY */ "Out of memory for allocating Intoid data bits.",
/* MSG_INTOIDS_CREDITS */ CreditsMessage,
/* MSG_INTOIDS_CONTINUE_SHOWING_ERRORS */ "Continue showing intoids.library error messages?",
/* MSG_INTOIDS_BAD_INPUT_PARMS */ "Bad function input parameters.",
/* MSG_INTOIDS_DIVIDE_BY_ZERO */ "Divide by zero.",
/* MSG_INTOIDS_PI_READ_ERROR */ "Error while reading portable integer data.",
/* MSG_INTOIDS_PI_WRITE_ERROR */ "Error while writing portable integer data.",
/* MSG_INTOIDS_PI_SEEK_ERROR */ "Error while seeking in portable integer data.",
/* MSG_INTOIDS_PI_BAD_FORMAT */ "Portable integer has unusual format, bad data?",
/* MSG_INTOIDS_UNKNOWN_MESSAGE */ "Unknown error message number used!"
};
/* English version of localizable strings used by GetIntoidsMessage. See
the IntoidStringNumbers typedef for the numbers. */
/******************************************************************************
* Things too large to be on the stack or that are global. These are put into
* the library base record and accessed via a dedicated register, thus a
* maximum of 32K of global near data. Mostly alphabetical order.
*/
struct IntuitionBase *IntuitionBase;
struct Library *LocaleBase;
struct Library *UtilityBase;
struct ExecBase *SysBase;
/* Various library base global variables. NULL if the library isn't open,
our custom startup/exit code opens them and closes them. */
struct Catalog *CurrentCatalog;
/* Used for finding language dependent string messages. NULL if not
available. Read about locale.library for details. */
struct Locale *CurrentLocale;
/* Identifies the current default locale from locale.library. NULL
if none available (use our own isspace() etc instead). */
BOOL DisplayErrors = TRUE;
/* TRUE if we are showing error messages, FALSE if the user has canceled
error message displays (remains FALSE until the library gets flushed from
memory). */
const char *ErrorMessagePntr = CreditsMessage;
/* Points to the current error message. If no error,
points to the credits string. */
/****** intoids.library/GetIntoidsMessage ***********************************
*
* NAME
* GetIntoidsMessage -- Convert a message number to a localised string.
*
* SYNOPSIS
* YourString = GetIntoidsMessage( StringNumber )
* D0 D0
*
* STRPTR GetIntoidsMessage( IntoidStringNumbers );
*
* FUNCTION
* Returns a pointer to a message which corresponds to the string
* number, the particular string depends on the user's language
* preferences. The string is obtained from the Intoids.catalog
* file if the user's language isn't English.
*
* INPUTS
* StringNumber - an enum from 0 to MSG_INTOIDS_MAX-1 that identifies
* the message you want the international string for. If you ask
* for a string that doesn't exist, you will get the string
* for MSG_INTOIDS_UNKNOWN_MESSAGE.
*
* RESULT
* YourString - a pointer to a read-only string which will be valid
* for as long as intoids.library is open.
*
* SEE ALSO
* locale.library - docs on how locale stuff works.
* Intoids.h - the enum of all message numbers.
*
*****************************************************************************
*/
STRPTR LIBFUNC GetIntoidsMessage (REGD0 IntoidStringNumbers StringNumber)
{
STRPTR ReturnValue;
if (StringNumber < 0 || StringNumber >= MSG_INTOIDS_MAX)
StringNumber = MSG_INTOIDS_UNKNOWN_MESSAGE;
ReturnValue = (STRPTR) (DefaultErrorStrings [StringNumber]);
if (CurrentCatalog != NULL)
ReturnValue = GetCatalogStr (CurrentCatalog,
(long) StringNumber, ReturnValue);
return ReturnValue;
}
/******************************************************************************
* Our runtime error message display function. If the user hits Cancel, no
* more messages will be displayed until the library is flushed from memory.
*/
void LOCALFUNC DisplayErrorMessage (const char *ErrorMessage)
{
struct IntuiText CancelIText =
{
AUTOFRONTPEN,
AUTOBACKPEN,
AUTODRAWMODE,
AUTOLEFTEDGE,
AUTOTOPEDGE,
AUTOITEXTFONT,
NULL, /* Message string goes here */
AUTONEXTTEXT
};
struct IntuiText ErrorIText = CancelIText;
struct IntuiText ExplainingIText = CancelIText;
struct IntuiText OKIText = CancelIText;
BOOL OKHit;
if (DisplayErrors)
{
ErrorIText.LeftEdge = BODYLEFTEDGE;
ErrorIText.TopEdge = BODYTOPEDGE;
ErrorIText.IText = (UBYTE *) ErrorMessage;
ErrorIText.NextText = &ExplainingIText;
ExplainingIText.LeftEdge = BODYLEFTEDGE;
ExplainingIText.TopEdge = BODYTOPEDGE + 12;
ExplainingIText.IText = (STRPTR)
GetIntoidsMessage (MSG_INTOIDS_CONTINUE_SHOWING_ERRORS);
ExplainingIText.NextText = NULL;
if (CurrentLocale != NULL)
{
CancelIText.IText = GetLocaleStr (CurrentLocale, NOSTR);
OKIText.IText = GetLocaleStr (CurrentLocale, YESSTR);
}
else
{
CancelIText.IText = (STRPTR) "Cancel";
OKIText.IText = (STRPTR) "OK";
}
OKHit = AutoRequest (NULL /* No parent window */,
&ErrorIText, &OKIText, &CancelIText,
0, 0, 512 /* width */, 80 /* height */);
if (!OKHit) /* Cancel selected. Stop showing errors. */
DisplayErrors = FALSE;
}
ErrorMessagePntr = ErrorMessage;
}
/****** intoids.library/SmallIntToIntoid ************************************
*
* NAME
* SmallIntToIntoid -- Macro to convert a small value int to an Intoid.
*
* SYNOPSIS
* MyIntoid = SmallIntToIntoid( ANumber )
*
* Intoid SmallIntToIntoid( int );
*
* FUNCTION
* Converts a short integer or char (unsigned or signed) to an Intoid.
* Will also work for long integers if they aren't larger than
* 0x3FFFFFFF in absolute value. Use LongToIntoid to properly handle
* larger longs.
*
* INPUTS
* ANumber - some signed or unsigned char, int or long.
*
* RESULT
* MyIntoid - the Intoid equivalent, as a small integer form Intoid.
* Never returns NULL since it doesn't do memory allocation.
*
* NOTES
* Only works for integers with absolute value of 0x3FFFFFFF or less.
* Defined in <libraries/Intoids.h>.
*
* SEE ALSO
* LongToIntoid().
*
*****************************************************************************
* looks like: define SmallIntToIntoid(x) ((Intoid) ((((long) (x)) << 1) | 1))
*/
/******************************************************************************
* Returns TRUE if the Intoid is a small integer, not a pointer.
* Small integers have the low bit set and the value in the rest of the bits.
*/
#define IntoidIsSmallInt(x) (((IntoidAsLong) x) & 1)
/******************************************************************************
* Returns TRUE if the Intoid is a pointer to an IntRep.
* The intoid is a pointer if it is a multiple of 4 (assume memory allocation
* is aligned on 4 or more byte boundaries (Amiga's AllocMem is 8)).
* Watch out for double evaluation side effects with this one!
*/
#define IntoidIsIntRep(x) (((x) != NULL) && ((((IntoidAsLong) (x)) & 3) == 0))
/******************************************************************************
* Returns TRUE if the Intoid contains a special code value. Things like
* infinity are special codes. Not-a-number is a special kind of special code,
* represented by a zero Intoid.
*/
#define IntoidIsSpecialCode(x) (((x) == NULL) || \
((((IntoidAsLong) (x)) & 3) == 2))
/******************************************************************************
* Returns an Intoid for the special code. Note that this doesn't work
* correctly for ISC_NOT_A_NUMBER (just use NULL instead). Precomputed
* for INTOID_POSITIVE_INFINITY and INTOID_NEGATIVE_INFINITY.
*/
#define SpecialCodeToIntoid(x) ((Intoid) (((long) (x)) * 4 + 2))
/******************************************************************************
* Returns the special code represented by the intoid. Also happens to
* work for not-a-number (NULL) Intoids.
*/
#define IntoidToSpecialCode(x) \
((IntoidSpecialCodes) (((IntoidAsLong) (x)) >> 2))
/******************************************************************************
* Returns the small integer an Intoid with the small integer coding
* represents. You should check with IntoidIsSmallInt before using this
* function.
*/
#define IntoidToSmallInt(x) (((IntoidAsLong) x) >> 1)
/******************************************************************************
* Get low order short's bits from a long. Originally this was an inline
* function that and'ed the value with I_MAXNUM, but that generated
* inefficient code with SAS/C (mostly due to casting back and forth).
*/
#define extract(x) ((unsigned short) (x))
/******************************************************************************
* Transfer high bits to low. Again, this used to be an inline function
* that did (x >> I_SHIFT) & I_MAXNUM.
*/
#define down(x) (x >> I_SHIFT)
/******************************************************************************
* Transfer low bits to high. Another previously inline function.
*/
#define up(x) (((unsigned long) (x)) << I_SHIFT)
/******************************************************************************
* Figure out max length of result of +, -, etc. Returns the larger length
* plus the pad value. Watch out, evaluates arguments twice.
*/
#define PadLargestLength(len1,len2,pad) \
((unsigned long) (((len1) >= (len2)) ? \
((unsigned long) (len1) + (unsigned long) (pad)) : \
((unsigned long) (len2) + (unsigned long) (pad))))
/******************************************************************************
* Compare two equal-length number arrays. Returns x - y, only sign is
* meaningful (<0 for x<y, ==0 for x==y, >0 for x>y).
*/
long LOCALFUNC __inline CompareNumberArrays (const unsigned short* x,
const unsigned short* y, unsigned short l)
{
long diff = 0;
const unsigned short* xs = &(x[l]);
const unsigned short* ys = &(y[l]);
while (l-- > 0 && (diff = (long) (*--xs) - (long) (*--ys)) == 0)
;
return diff;
}
/******************************************************************************
* Ensure array length & sign are correct (removes leading zeroes from number).
*/
void LOCALFUNC __inline Icheck (IntRepPointer rep)
{
unsigned short l = rep->currentLength;
const unsigned short* p = &(rep->numberArray[l]);
while (l != 0 && *--p == 0)
--l;
if ((rep->currentLength = (unsigned short) l) == 0)
rep->positiveSign = I_POSITIVE;
}
/******************************************************************************
* Zero out the end of a rep, including allocated but unused space.
*/
void LOCALFUNC __inline Iclear_from (IntRepPointer rep, unsigned short p)
{
unsigned short* cp = &(rep->numberArray[p]);
const unsigned short* cf = &(rep->numberArray[rep->allocatedLength]);
while(cp < cf)
*cp++ = 0;
}
/******************************************************************************
* Zero out the part of a rep, from the given spot up to but not including
* the other spot's index.
*/
void LOCALFUNC __inline Iclear_from_to (IntRepPointer rep,
unsigned short From, unsigned short To)
{
unsigned short* cp = &(rep->numberArray[From]);
const unsigned short* cf = &(rep->numberArray[To]);
while(cp < cf)
*cp++ = 0;
}
/******************************************************************************
* Copy some of the number part of a rep to another. Like memcpy but copies
* shorts rather than bytes and the arguments are reversed.
*/
void LOCALFUNC __inline scpy (const unsigned short* src,
unsigned short* dest, unsigned short nb)
{
while (nb-- > 0)
*dest++ = *src++;
}
/******************************************************************************
* Allocate a new Irep with the given number of shorts available for storage.
* Returns NULL if it fails. Contents will be initialised to value for zero.
*/
IntRepPointer LOCALFUNC Inew (unsigned long newlen)
{
IntRepPointer rep;
unsigned long siz;
if (newlen > MAXIntRep_SIZE)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_NEW_TOO_BIG));
return NULL;
}
if (newlen < MINIntRep_SIZE)
newlen = MINIntRep_SIZE;
siz = sizeof(IntRep) +
((unsigned long) newlen - 1 /* IntRep includes one short */) * sizeof(short);
rep = AllocMem (siz,
MEMF_PUBLIC | MEMF_CLEAR /* Initialise new memory to zero */);
if (rep == NULL)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_INTREP_OUT_OF_MEMORY));
return NULL;
}
rep->allocatedLength = newlen;
rep->positiveSign = I_POSITIVE;
return rep;
}
/******************************************************************************
* Deallocate an Irep.
*/
void LOCALFUNC Idelete (IntRepPointer DeleteMe)
{
unsigned long OldLength;
if (DeleteMe != NULL && DeleteMe->allocatedLength != 0)
{
OldLength = DeleteMe->allocatedLength;
DeleteMe->allocatedLength = 0; /* Just for debugging and safety. */
FreeMem (DeleteMe, sizeof (IntRep) +
(OldLength - 1 /* IntRep struct includes one short */) * sizeof (short));
}
}
/******************************************************************************
* Internal function for resizing an IntRep, allocating space if it can't
* reuse the existing space. Returns NULL if it fails. CurrentLength is set
* to the new length and any extra words in the number are assumed to be zero
* (new leading digits are zero). Also zeroes the whole number if requested.
*/
IntRepPointer LOCALFUNC Iresize (IntRepPointer RecycleMe,
unsigned long NewLength, BOOL ZeroNewValue)
{
IntRepPointer NewRep;
unsigned short OldLength;
if (RecycleMe == NULL)
NewRep = Inew (NewLength); /* Automatically zero value if successful. */
else
{
OldLength = RecycleMe->currentLength;
if (NewLength > RecycleMe->allocatedLength)
{
NewRep = Inew (NewLength);
if (NewRep != NULL && !ZeroNewValue)
{
scpy (RecycleMe->numberArray, NewRep->numberArray, OldLength);
NewRep->positiveSign = RecycleMe->positiveSign;
}
Idelete (RecycleMe);
}
else /* Reusing the IntRep. Adjust it to the new size. */
{
NewRep = RecycleMe;
if (ZeroNewValue)
{
/* Just clear the non-zero part of the old one. */
Iclear_from_to (NewRep, 0, OldLength);
NewRep->positiveSign = I_POSITIVE;
}
else /* Just clear parts that shrink in size. */
Iclear_from_to (NewRep, (unsigned short) NewLength, OldLength);
}
}
if (NewRep != NULL)
NewRep->currentLength = NewLength;
return NewRep;
}
/******************************************************************************
* Internal function for copying an IntRep to another one, allocating space
* if it can't overwrite the old one. Handles NULL pointers and duplicate
* pointers. Returns NULL if it fails. Consider the old one deallocated
* after this function.
*/
IntRepPointer LOCALFUNC Icopy (IntRepPointer old, const IntRepPointer src)
{
unsigned short NewLength;
IntRepPointer rep;
if (old == src && old != NULL)
return old;
if (src == NULL) /* Source doesn't exist. */
{
if (old == NULL)
return Inew (0 /* Minimum size will be used, new's value is zero */);
Iclear_from_to (old, 0, old->currentLength);
old->currentLength = 0;
old->positiveSign = I_POSITIVE;
return old;
}
/* Source exists. Copy it. */
NewLength = src->currentLength;
if (old == NULL || NewLength > old->allocatedLength)
{
if (old != NULL) Idelete (old);
rep = Inew ((unsigned long) NewLength);
if (rep == NULL) return NULL;
}
else /* Can reuse the old one. */
{
/* Zero out unused things if old shrinks to new. */
Iclear_from_to (old, NewLength, old->currentLength);
rep = old;
}
rep->currentLength = NewLength;
rep->positiveSign = src->positiveSign;
scpy (src->numberArray, rep->numberArray, NewLength);
return rep;
}
/****** intoids.library/FreeIntoid ******************************************
*
* NAME
* FreeIntoid -- Deallocates an Intoid.
*
* SYNOPSIS
* FreeIntoid( RecycleMe )
* A0
*
* void FreeIntoid( Intoid );
*
* FUNCTION
* Deallocates memory used by the given Intoid. OK to pass in NULL.
* Like regular pointers, you shouldn't use the value in RecycleMe any
* more after calling this function.
*
* INPUTS
* RecycleMe - a previously allocated Intoid or NULL.
*
*****************************************************************************
*/
VOID LIBFUNC FreeIntoid (REGA0 Intoid RecycleMe)
{
if (IntoidIsIntRep (RecycleMe))
{
Idelete ((IntoidAsPointer) RecycleMe);
}
}
/******************************************************************************
* Returns an Intoid set up as a pointer to an IntRep which contains at least
* NewLength shorts in it's numberArray and currentLength set to NewLength.
* If possible, the memory from RecycleMe is reused. The new Intoid is set to
* positive zero if requested, otherwise the value is copied from RecycleMe.
* Returns NULL if it fails (RecycleMe is still deallocated in that case).
*/
Intoid LOCALFUNC ResizeIntoid (Intoid RecycleMe, unsigned long NewLength,
BOOL ZeroNewValue)
{
IntRepPointer NewIntoid;
NewIntoid = Iresize (IntoidIsIntRep (RecycleMe) ?
(IntoidAsPointer) RecycleMe : NULL, NewLength, ZeroNewValue);
return NewIntoid;
}
/******************************************************************************
* Internal function for adding a Long to an IntRep based Intoid. If NegateB
* is TRUE then the negative of IntegerB will be added. Handles the case
* where RecycleMe is the same as IntegerB, avoiding extra allocations.
* Doesn't normalize the result, so the result is always an IntRep or NULL.
*/
Intoid LOCALFUNC AddLongAndIntRep (long IntegerA, Intoid IntegerB,
BOOL NegateB, Intoid RecycleMe)
{
long ComparisonResult;
unsigned short *DataResult;
unsigned short *Data1Source;
unsigned short *Data1SourceEnd;
unsigned short *Data2Source;
unsigned short *Data2SourceEnd;
unsigned short LengthA;
unsigned short LengthB;
IntRepPointer RepB;
IntRepPointer RepR;
Intoid Result;
BOOL SameBR;
BOOL SignA;
BOOL SignB;
unsigned short TempNumberArray [SHORT_PER_LONG];
unsigned long ULongA;
unsigned long ULongSum;
if (!IntoidIsIntRep (IntegerB))
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_BAD_INPUT_PARMS));
FreeIntoid (RecycleMe);
return NULL; /* NULL input or wrong kind of intoid. */
}
RepB = (IntoidAsPointer) IntegerB;
LengthB = RepB->currentLength;
SignB = (NegateB && LengthB != 0) ? !RepB->positiveSign : RepB->positiveSign;
SameBR = (IntegerB == RecycleMe);
SignA = (IntegerA >= 0);
ULongA = SignA ? IntegerA : -IntegerA;
if (SignA == SignB) /* Just add the two unsigned values. */
{
Result = ResizeIntoid (RecycleMe,
PadLargestLength (LengthB, SHORT_PER_LONG,
1 /* Plus one extra short for overflows */),
!SameBR /* Zero result if not same as IntegerB, else keep old value */);
if (Result == NULL) return NULL; /* Out of memory. */
RepR = (IntoidAsPointer) Result;
RepR->positiveSign = SignB;
DataResult = RepR->numberArray;
Data1Source = SameBR ? DataResult : RepB->numberArray;
Data1SourceEnd = Data1Source + LengthB;
ULongSum = 0;
while (Data1Source < Data1SourceEnd && ULongA != 0)
{
ULongSum += (unsigned long) (*Data1Source++) + extract (ULongA);
ULongA = down (ULongA);
*DataResult++ = extract (ULongSum);
ULongSum = down (ULongSum);
}
while (ULongA != 0) /* Data source has run out first. */
{
ULongSum += extract (ULongA);
ULongA = down (ULongA);
*DataResult++ = extract (ULongSum);
ULongSum = down (ULongSum);
}
while (ULongSum != 0 && Data1Source < Data1SourceEnd)
{
ULongSum += (unsigned long) (*Data1Source++);
*DataResult++ = extract (ULongSum);
ULongSum = down (ULongSum);
}
if (ULongSum != 0)
*DataResult = extract (ULongSum); /* Overflowed after adding numbers. */
else if (DataResult != Data1Source) /* Copy rest of number if needed. */
while (Data1Source < Data1SourceEnd)
*DataResult++ = *Data1Source++;
}
else /* Doing a subtraction. */
{
/* Put the long value into an array of shorts format. */
LengthA = 0;
while (ULongA != 0)
{
TempNumberArray[LengthA++] = extract (ULongA);
ULongA = down (ULongA);
}
/* See which one is bigger, IntegerA or IntegerB. */
ComparisonResult = (long) LengthB - (long) LengthA;
if (ComparisonResult == 0) /* Same length, compare values. */
ComparisonResult = CompareNumberArrays (RepB->numberArray,
TempNumberArray, LengthA);
if (ComparisonResult == 0) /* Result should be zero. */
{
Result = ResizeIntoid (RecycleMe, 0, TRUE /* zero it */);
if (Result == NULL) return NULL; /* Out of memory. */
RepR = (IntoidAsPointer) Result;
}
else /* Do the math. */
{
Result = ResizeIntoid (RecycleMe,
PadLargestLength (LengthB, SHORT_PER_LONG,
0 /* no chance of overflow so no pad */),
!SameBR /* Zero result if not same as IntegerB, else keep old value */);
if (Result == NULL) return NULL; /* Out of memory. */
RepR = (IntoidAsPointer) Result;
DataResult = RepR->numberArray;
if (ComparisonResult > 0) /* IntegerB > IntegerA */
{
Data1Source = SameBR ? DataResult : RepB->numberArray;
Data1SourceEnd = Data1Source + LengthB;
Data2Source = TempNumberArray;
Data2SourceEnd = Data2Source + LengthA;
RepR->positiveSign = SignB;
}
else /* IntegerB < IntegerA */
{
Data1Source = TempNumberArray;
Data1SourceEnd = Data1Source + LengthA;
Data2Source = SameBR ? DataResult : RepB->numberArray;
Data2SourceEnd = Data2Source + LengthB;
RepR->positiveSign = SignA;
}
/* Calculate (1 << Largest Length) + Data1Source - Data2Source.
The (1 << Largest Length) is too big to fit in the result,
so it seems to be zero as far as the result is concerned.
It's a way of doing the borrows without using negative numbers. */
ULongSum = 1;
while (Data2Source < Data2SourceEnd)
{
ULongSum += (unsigned long) (*Data1Source++) + I_MAXNUM -
(unsigned long) (*Data2Source++);
*DataResult++ = extract (ULongSum);
ULongSum = down (ULongSum);
}
/* Finish off the Data1Source, which is known bigger than Data2Source. */
while (ULongSum == 0 && Data1Source < Data1SourceEnd)
{
ULongSum = (unsigned long) (*Data1Source++) + I_MAXNUM;
*DataResult++ = extract (ULongSum);
ULongSum = down (ULongSum);
}
if (DataResult != Data1Source) /* Copy rest of number if needed. */
while (Data1Source < Data1SourceEnd)
*DataResult++ = *Data1Source++;
}
}
Icheck (RepR);
return Result;
}
/******************************************************************************
* Internal function for adding two IntRep based Intoids. If NegateX is TRUE
* then the negative of the appropriate IntegerX will be added. Handles the
* case where RecycleMe is the same as IntegerX, avoiding extra allocations.
* Doesn't normalize the result, so the result is always an IntRep or NULL.
*/
Intoid LOCALFUNC AddIntRepAndIntRep (Intoid IntegerA, BOOL NegateA,
Intoid IntegerB, BOOL NegateB, Intoid RecycleMe)
{
long ComparisonResult;
unsigned short *DataResult;
unsigned short *Data1Source;
unsigned short *Data1SourceEnd;
unsigned short *Data2Source;
unsigned short *Data2SourceEnd;
unsigned short LengthA;
unsigned short LengthB;
IntRepPointer RepA;
IntRepPointer RepB;
IntRepPointer RepR;
Intoid Result;
BOOL SameAR;
BOOL SameBR;
BOOL SignA;
BOOL SignB;
unsigned long ULongSum;
if (!IntoidIsIntRep (IntegerA) || !IntoidIsIntRep (IntegerB))
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_BAD_INPUT_PARMS));
FreeIntoid (RecycleMe);
return NULL; /* NULL input or wrong kind of intoid. */
}
RepA = (IntoidAsPointer) IntegerA;
RepB = (IntoidAsPointer) IntegerB;
LengthA = RepA->currentLength;
LengthB = RepB->currentLength;
SignA = (NegateA && LengthA != 0) ? !RepA->positiveSign : RepA->positiveSign;
SignB = (NegateB && LengthB != 0) ? !RepB->positiveSign : RepB->positiveSign;
SameAR = (IntegerA == RecycleMe);
SameBR = (IntegerB == RecycleMe);
if (SignA == SignB)
{
Result = ResizeIntoid (RecycleMe,
PadLargestLength (LengthA, LengthB,
1 /* Plus one extra short for overflows */),
!(SameAR || SameBR) /* Zero result if result is not an input */);
if (Result == NULL) return NULL; /* Out of memory. */
RepR = (IntoidAsPointer) Result;
RepR->positiveSign = SignA;
DataResult = RepR->numberArray;
/* Associate the longer number with Data1Source. */
if (LengthA >= LengthB)
{
Data1Source = SameAR ? RepR->numberArray : RepA->numberArray;
Data1SourceEnd = Data1Source + LengthA;
Data2Source = SameBR ? RepR->numberArray : RepB->numberArray;
Data2SourceEnd = Data2Source + LengthB;
}
else /* IntegerB is longer, it is used as Data1Source. */
{
Data1Source = SameBR ? RepR->numberArray : RepB->numberArray;
Data1SourceEnd = Data1Source + LengthB;
Data2Source = SameAR ? RepR->numberArray : RepA->numberArray;
Data2SourceEnd = Data2Source + LengthA;
}
ULongSum = 0;
while (Data2Source < Data2SourceEnd)
{
ULongSum += (unsigned long) (*Data1Source++) +
(unsigned long) (*Data2Source++);
*DataResult++ = extract (ULongSum);
ULongSum = down (ULongSum);
}
while (ULongSum != 0 && Data1Source < Data1SourceEnd)
{
ULongSum += (unsigned long) (*Data1Source++);
*DataResult++ = extract (ULongSum);
ULongSum = down (ULongSum);
}
if (ULongSum != 0) /* Overflowed into last short. */
*DataResult = extract (ULongSum);
else /* Copy unchanging high part to result if needed. */
if (DataResult != Data1Source)
while (Data1Source < Data1SourceEnd)
*DataResult++ = *Data1Source++;
}
else /* Different signs, do a subtraction. */
{
/* See which one is bigger, IntegerA or IntegerB. */
ComparisonResult = (long) LengthA - (long) LengthB;
if (ComparisonResult == 0) /* Same length, compare values. */
ComparisonResult = CompareNumberArrays (RepA->numberArray,
RepB->numberArray, LengthA);
if (ComparisonResult == 0) /* Result should be zero. */
{
Result = ResizeIntoid (RecycleMe, 0, TRUE /* zero it */);
if (Result == NULL) return NULL; /* Out of memory. */
RepR = (IntoidAsPointer) Result;
}
else /* Do the math. */
{
Result = ResizeIntoid (RecycleMe,
PadLargestLength (LengthA, LengthB,
0 /* no need for overflow padding */),
!(SameAR || SameBR) /* Zero result if result is not an input */);
if (Result == NULL) return NULL; /* Out of memory. */
RepR = (IntoidAsPointer) Result;
DataResult = RepR->numberArray;
/* Put the larger in magnitude integer in Data1Source. */
if (ComparisonResult > 0) /* If IntegerA > IntegerB */
{
Data1Source = SameAR ? DataResult : RepA->numberArray;
Data1SourceEnd = Data1Source + LengthA;
Data2Source = SameBR ? DataResult : RepB->numberArray;
Data2SourceEnd = Data2Source + LengthB;
RepR->positiveSign = SignA;
}
else /* IntegerA < IntegerB */
{
Data1Source = SameBR ? DataResult : RepB->numberArray;
Data1SourceEnd = Data1Source + LengthB;
Data2Source = SameAR ? DataResult : RepA->numberArray;
Data2SourceEnd = Data2Source + LengthA;
RepR->positiveSign = SignB;
}
/* Calculate (1 << Largest Length) + DataSource - Data2Source.
The (1 << Largest Length) is too big to fit in the result,
so it seems to be zero as far as the result is concerned.
It's a way of doing the borrows without using negative numbers. */
ULongSum = 1;
while (Data2Source < Data2SourceEnd)
{
ULongSum += (unsigned long) (*Data1Source++) + I_MAXNUM -
(unsigned long) (*Data2Source++);
*DataResult++ = extract (ULongSum);
ULongSum = down (ULongSum);
}
/* Finish off the Data1Source, which is known bigger than Data2Source. */
while (ULongSum == 0 && Data1Source < Data1SourceEnd)
{
ULongSum = (unsigned long) (*Data1Source++) + I_MAXNUM;
*DataResult++ = extract (ULongSum);
ULongSum = down (ULongSum);
}
if (DataResult != Data1Source) /* Copy rest of number if needed. */
while (Data1Source < Data1SourceEnd)
*DataResult++ = *Data1Source++;
}
}
Icheck (RepR);
return Result;
}
/******************************************************************************
* Multiply a long and an IntRep based Intoid.
* Returns an IntRep type Intoid or NULL.
*/
Intoid LOCALFUNC MultiplyLongAndIntRep (long IntegerA, const Intoid IntegerB,
Intoid RecycleMe)
{
unsigned short *Data1Source;
unsigned short *Data1SourceStart;
unsigned short *Data2Source;
unsigned short *Data2SourceEnd;
unsigned short *Data2SourceStart;
unsigned short *DataResult;
unsigned short *DataResultCurrent;
unsigned short *DataResultEnd;
unsigned short LengthA;
unsigned short LengthB;
unsigned long LengthR;
unsigned long Multiplier;
IntRepPointer RepB;
IntRepPointer RepR;
Intoid Result;
BOOL SameBR;
BOOL SignA;
BOOL SignB;
BOOL SignR;
unsigned long Sum;
unsigned short TempNumberArray [SHORT_PER_LONG];
unsigned long ULongA;
/* Get ahold of the IntRep for IntegerB. */
if (!IntoidIsIntRep (IntegerB))
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_BAD_INPUT_PARMS));
FreeIntoid (RecycleMe);
return NULL; /* NULL input or wrong kind of intoid. */
}
RepB = (IntoidAsPointer) IntegerB;
LengthB = RepB->currentLength;
SameBR = (IntegerB == RecycleMe);
/* Check for the simple case of multiplying by zero. */
if (LengthB == 0 || IntegerA == 0)
{
/* Multiplying by zero gives zero. */
return ResizeIntoid (RecycleMe, 0, TRUE /* zero it */);
}
/* Check for the simple case of multiplying by one. */
if (IntegerA == 1)
{
/* Multiplying by one gives the same number. Use ResizeIntoid
so that RecycleMe is guaranteed to be an IntRep. Should
also work if RecycleMe == IntegerB. */
RecycleMe = ResizeIntoid (RecycleMe, (unsigned long) LengthB, FALSE);
return Icopy (RecycleMe, IntegerB);
}
/* Figure out the sign of the result, before we start mangling it. */
SignA = (IntegerA >= 0);
SignB = RepB->positiveSign;
SignR = (SignA == SignB);
/* Copy our long number into a temporary array of shorts, and
find out how long it is. */
ULongA = SignA ? IntegerA : -IntegerA;
LengthA = 0;
while (ULongA != 0)
{
TempNumberArray[LengthA++] = extract (ULongA);
ULongA = down (ULongA);
}
/* Allocate space for the result. */
LengthR = (unsigned long) LengthA + (unsigned long) LengthB;
Result = ResizeIntoid (RecycleMe, LengthR,
!SameBR /* Zero result if not same as IntegerB, else keep old value */);
if (Result == NULL) return NULL; /* Out of memory. */
RepR = (IntoidAsPointer) Result;
RepR->positiveSign = SignR;
/* Set up pointers to sources and destinations. */
DataResult = RepR->numberArray;
DataResultEnd = DataResult + LengthR;
if (SameBR)
{
DataResultCurrent = DataResult + (LengthB - 1);
Data1SourceStart = DataResult;
Data1Source = DataResultCurrent;
Data2SourceStart = TempNumberArray;
Data2SourceEnd = Data2SourceStart + LengthA;
}
else if (LengthB <= LengthA)
{
DataResultCurrent = DataResult + (LengthB - 1);
Data1SourceStart = RepB->numberArray;
Data1Source = Data1SourceStart + (LengthB - 1);
Data2SourceStart = TempNumberArray;
Data2SourceEnd = Data2SourceStart + LengthA;
}
else
{
DataResultCurrent = DataResult + (LengthA - 1);
Data1SourceStart = TempNumberArray;
Data1Source = Data1SourceStart + (LengthA - 1);
Data2SourceStart = RepB->numberArray;
Data2SourceEnd = Data2SourceStart + LengthB;
}
/* And now the big multiplication. Much like you do in school grade 2. */
while (Data1Source >= Data1SourceStart)
{
Multiplier = (unsigned long) (*Data1Source--);
DataResult = DataResultCurrent--;
*DataResult = 0;
if (Multiplier != 0)
{
Sum = 0;
Data2Source = Data2SourceStart;
while (Data2Source < Data2SourceEnd)
{
Sum += Multiplier * (unsigned long) (*Data2Source++) +
(unsigned long) (*DataResult);
*DataResult++ = extract (Sum);
Sum = down (Sum);
}
while (Sum != 0 && DataResult < DataResultEnd)
{
Sum += (unsigned long) (*DataResult);
*DataResult++ = extract (Sum);
Sum = down (Sum);
}
}
}
Icheck (RepR);
return Result;
}
/******************************************************************************
* Multiply two IntReps, returns an IntRep or NULL.
* Assumes trivial cases done before this (arguments aren't 0 or +-1).
* Recycles RecycleMe if it isn't NULL, can be the same as either argument or
* both (special case code for squaring).
*/
IntRepPointer LOCALFUNC MultiplyIntRepAndIntRep (const IntRepPointer x,
const IntRepPointer y, Intoid RecycleMe)
{
IntRepPointer r;
unsigned short rl; /* Result length. */
unsigned long rll; /* Long version of length to catch overflows. */
unsigned short xl;
unsigned short yl;
int rsgn;
int xrsame;
int yrsame;
int xysame;
if (!IntoidIsIntRep (x) || !IntoidIsIntRep (y))
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_BAD_INPUT_PARMS));
FreeIntoid (RecycleMe);
return NULL;
}
r = IntoidIsIntRep (RecycleMe) ? RecycleMe : NULL;
xl = x->currentLength;
yl = y->currentLength;
rll = (unsigned long) xl + (unsigned long) yl;
rl = rll;
rsgn = x->positiveSign == y->positiveSign;
xrsame = x == r;
yrsame = y == r;
xysame = x == y;
if (!(xysame && xrsame))
{
unsigned short* rs;
unsigned short* topr;
unsigned short* currentr;
const unsigned short* bota;
const unsigned short* as;
const unsigned short* botb;
const unsigned short* topb;
r = Iresize (r, rll, !(xrsame || yrsame) /* Zero if different vars */);
if (r == NULL) return NULL;
rs = r->numberArray;
topr = &(rs[rl]);
/* use best inner/outer loop params given constraints */
if (xrsame)
{
currentr = &(rs[xl-1]);
bota = rs;
as = currentr;
botb = y->numberArray;
topb = &(botb[yl]);
}
else if (yrsame)
{
currentr = &(rs[yl-1]);
bota = rs;
as = currentr;
botb = x->numberArray;
topb = &(botb[xl]);
}
else if (xl <= yl)
{
currentr = &(rs[xl-1]);
bota = x->numberArray;
as = &(bota[xl-1]);
botb = y->numberArray;
topb = &(botb[yl]);
}
else
{
currentr = &(rs[yl-1]);
bota = y->numberArray;
as = &(bota[yl-1]);
botb = x->numberArray;
topb = &(botb[xl]);
}
while (as >= bota)
{
unsigned long ai = (unsigned long)(*as--);
unsigned short* rs = currentr--;
*rs = 0;
if (ai != 0)
{
unsigned long sum = 0;
const unsigned short* bs = botb;
while (bs < topb)
{
sum += ai * (unsigned long)(*bs++) + (unsigned long)(*rs);
*rs++ = extract(sum);
sum = down(sum);
}
while (sum != 0 && rs < topr)
{
sum += (unsigned long)(*rs);
*rs++ = extract(sum);
sum = down(sum);
}
}
}
}
else /* x, y, and r same; compute over diagonals (squaring a number) */
{
unsigned short* botr;
unsigned short* topr;
unsigned short* rs;
const unsigned short* bota;
const unsigned short* loa;
const unsigned short* hia;
r = Iresize(r, rll, FALSE);
if (r == NULL) return NULL;
botr = r->numberArray;
topr = &(botr[rl]);
rs = &(botr[rl - 2]);
bota = (xrsame)? botr : x->numberArray;
loa = &(bota[xl - 1]);
hia = loa;
for (; rs >= botr; --rs)
{
const unsigned short* h = hia;
const unsigned short* l = loa;
unsigned long prod = (unsigned long)(*h) * (unsigned long)(*l);
*rs = 0;
for(;;)
{
unsigned short* rt = rs;
unsigned long sum = prod + (unsigned long)(*rt);
*rt++ = extract(sum);
sum = down(sum);
while (sum != 0 && rt < topr)
{
sum += (unsigned long)(*rt);
*rt++ = extract(sum);
sum = down(sum);
}
if (h > l)
{
rt = rs;
sum = prod + (unsigned long)(*rt);
*rt++ = extract(sum);
sum = down(sum);
while (sum != 0 && rt < topr)
{
sum += (unsigned long)(*rt);
*rt++ = extract(sum);
sum = down(sum);
}
if (--h >= ++l)
prod = (unsigned long)(*h) * (unsigned long)(*l);
else
break;
}
else
break;
}
if (loa > bota)
--loa;
else
--hia;
}
}
r->positiveSign = rsgn;
Icheck(r);
return r;
}
/******************************************************************************
* Divide by single digit, return remainder.
* If q != 0, then keep the result in q, else just compute remainder.
*/
unsigned short LOCALFUNC unscale (const unsigned short* x, unsigned short xl,
unsigned short y, unsigned short* q)
{
if (xl == 0 || y == 1)
return 0;
else if (q != 0)
{
unsigned short* botq = q;
unsigned short* qs = &(botq[xl - 1]);
const unsigned short* xs = &(x[xl - 1]);
unsigned long rem = 0;
while (qs >= botq)
{
unsigned long u;
rem = up(rem) | *xs--;
#if _AMIGA && __SASC /* Use the utility.library 32 bit math subroutines. */
u = UDivMod32 (rem, (unsigned long) y);
rem = getreg (REG_D1);
#else /* Use compiler's 32 bit math. */
u = rem / y;
rem -= u * y;
#endif
*qs-- = extract(u);
}
return (unsigned short) extract(rem);
}
else /* Same loop, a bit faster if just need rem. */
{
const unsigned short* botx = x;
const unsigned short* xs = &(botx[xl - 1]);
unsigned long rem = 0;
while (xs >= botx)
{
rem = up(rem) | *xs--;
#if _AMIGA && __SASC /* Use the utility.library 32 bit math subroutines. */
UDivMod32 (rem, (unsigned long) y);
rem = getreg (REG_D1);
#else /* Use compiler's 32 bit math. */
rem -= (rem / y) * y;
#endif
}
return (unsigned short) extract(rem);
}
}
/******************************************************************************
* Core division routine.
*/
void LOCALFUNC do_divide (unsigned short* rs,
const unsigned short* ys, unsigned short yl,
unsigned short* qs, unsigned short ql)
{
const unsigned short* topy = &(ys[yl]);
unsigned short d1 = ys[yl - 1];
unsigned short d2 = ys[yl - 2];
int l = ql - 1;
int i = l + yl;
for (; l >= 0; --l, --i)
{
unsigned short qhat; /* guess q */
if (d1 == rs[i])
qhat = I_MAXNUM;
else
{
unsigned long lr = up((unsigned long)rs[i]) | rs[i-1];
qhat = lr / d1;
}
for(;;) /* adjust q, use CompareNumberArrays to avoid overflow problems */
{
unsigned short ts[3];
unsigned long prod = (unsigned long)d2 * (unsigned long)qhat;
ts[0] = extract(prod);
prod = down(prod) + (unsigned long)d1 * (unsigned long)qhat;
ts[1] = extract(prod);
ts[2] = extract(down(prod));
if (CompareNumberArrays(ts, &(rs[i-2]), 3) > 0)
--qhat;
else
break;
};
/* multiply & subtract */
{
const unsigned short* yt = ys;
unsigned short* rt = &(rs[l]);
unsigned long prod = 0;
unsigned long hi = 1;
while (yt < topy)
{
prod = (unsigned long)qhat * (unsigned long)(*yt++) + down(prod);
hi += (unsigned long)(*rt) + I_MAXNUM - (unsigned long)(extract(prod));
*rt++ = extract(hi);
hi = down(hi);
}
hi += (unsigned long)(*rt) + I_MAXNUM - (unsigned long)(down(prod));
*rt = extract(hi);
hi = down(hi);
/* off-by-one, add back */
if (hi == 0)
{
--qhat;
yt = ys;
rt = &(rs[l]);
hi = 0;
while (yt < topy)
{
hi = (unsigned long)(*rt) + (unsigned long)(*yt++) + down(hi);
*rt++ = extract(hi);
}
*rt = 0;
}
if (qs != 0)
qs[l] = qhat;
}
}
}
/******************************************************************************
* Divides an IntRep by a long. Returns an IntRep or NULL. Assumes
* y != 0, x > y in magnitude (trivial cases done already).
*/
IntRepPointer LOCALFUNC DivideIntRepByLong (const IntRepPointer x, long y,
Intoid RecycleMe)
{
IntRepPointer q;
unsigned short xl;
unsigned short ys[SHORT_PER_LONG];
unsigned long u;
int xsgn;
int ysgn;
int samesign;
unsigned short yl;
long ql;
if (!IntoidIsIntRep (x))
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_BAD_INPUT_PARMS));
FreeIntoid (RecycleMe);
return NULL;
}
q = IntoidIsIntRep (RecycleMe) ? (IntoidAsPointer) RecycleMe : NULL;
xl = x->currentLength;
xsgn = x->positiveSign;
ysgn = y >= 0;
samesign = xsgn == ysgn;
yl = 0;
if (ysgn)
u = y;
else
u = -y;
while (u != 0)
{
ys[yl++] = extract(u);
u = down(u);
}
if (yl == 1) /* If divisor fits in a short. */
{
q = Icopy(q, x);
if (q == NULL) return NULL;
unscale(q->numberArray, q->currentLength, ys[0], q->numberArray);
}
else
{
IntRepPointer r = NULL;
unsigned short prescale = (I_RADIX / (1 + ys[yl - 1]));
if (prescale != 1)
{
unsigned long prod = (unsigned long)prescale * (unsigned long)ys[0];
ys[0] = extract(prod);
prod = down(prod) + (unsigned long)prescale * (unsigned long)ys[1];
ys[1] = extract(prod);
r = MultiplyLongAndIntRep (((long)prescale & I_MAXNUM), x, r);
if (r == NULL)
{
Idelete (q);
return NULL;
}
}
else
{
r = Iresize (r, xl + 1L, TRUE /* zero it */);
if (r == NULL)
{
Idelete (q);
return NULL;
}
scpy(x->numberArray, r->numberArray, xl);
}
ql = xl - yl + 1L;
q = Iresize (q, ql, TRUE /* zero it */);
if (q == NULL)
{
Idelete (r);
return NULL;
}
do_divide(r->numberArray, ys, yl, q->numberArray, (unsigned short) ql);
Idelete (r);
}
q->positiveSign = samesign;
Icheck(q);
return q;
}
/******************************************************************************
* Divides an IntRep by another IntRep, returns an IntRep or NULL. Assumes
* y != 0, x > y in magnitude (trivial cases done already).
*/
IntRepPointer LOCALFUNC DivideIntRepByIntRep (const IntRepPointer x,
const IntRepPointer y, Intoid RecycleMe)
{
IntRepPointer q;
unsigned short xl;
unsigned short yl;
int xsgn;
int ysgn;
int samesign;
long ql;
if (!IntoidIsIntRep (x) || !IntoidIsIntRep (y))
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_BAD_INPUT_PARMS));
FreeIntoid (RecycleMe);
return NULL;
}
q = IntoidIsIntRep (RecycleMe) ? (IntoidAsPointer) RecycleMe : NULL;
xl = x->currentLength;
yl = y->currentLength;
xsgn = x->positiveSign;
ysgn = y->positiveSign;
samesign = xsgn == ysgn;
if (yl == 1) /* Fits in a short. */
{
q = Icopy(q, x);
if (q == NULL) return NULL;
unscale(q->numberArray, q->currentLength,
y->numberArray[0], q->numberArray);
}
else
{
IntRepPointer yy = NULL;
IntRepPointer r = NULL;
unsigned short prescale = (I_RADIX / (1 + y->numberArray[yl - 1]));
if (prescale != 1 || y == q)
{
yy = MultiplyLongAndIntRep (((long)prescale & I_MAXNUM), y, yy);
r = MultiplyLongAndIntRep (((long)prescale & I_MAXNUM), x, r);
if (yy == NULL || r == NULL)
{
Idelete (r);
Idelete (yy);
Idelete (q);
return NULL;
}
}
else
{
yy = (IntRepPointer) y;
r = Iresize (r, xl + 1L, TRUE /* zero it */);
if (r == NULL)
{
Idelete (q);
return NULL;
}
scpy(x->numberArray, r->numberArray, xl);
}
ql = xl - yl + 1L;
q = Iresize (q, ql, TRUE /* zero it */);
if (q == NULL)
{
Idelete (r);
if (yy != y) Idelete (yy);
return NULL;
}
do_divide(r->numberArray, yy->numberArray, yl, q->numberArray,
(unsigned short) ql);
if (yy != y) Idelete (yy);
Idelete (r);
}
q->positiveSign = samesign;
Icheck(q);
return q;
}
/****** intoids.library/GetLastIntoidErrorMessage ***************************
*
* NAME
* GetLastIntoidErrorMessage -- Returns the latest error message.
*
* SYNOPSIS
* MyString = GetLastIntoidErrorMessage( )
* D0
*
* STRPTR GetLastIntoidErrorMessage( VOID );
*
* FUNCTION
* Returns a pointer to the last error message. Maybe be incorrect if
* you are using multitasking to cause errors (you get the global latest
* error, not your latest error). Also resets the error message to be
* the copyright and credits for this library (you get that message if
* there hasn't been an error since the last call to this function).
*
* RESULT
* MyString - a pointer to a read only string in your favorite language.
*
* NOTES
* The same message was displayed to the user in a system requester,
* or maybe not if that feature is turned off by the user.
*
*****************************************************************************
*/
STRPTR LIBFUNC GetLastIntoidErrorMessage (VOID)
{
STRPTR ReturnPointer;
ReturnPointer = (STRPTR) ErrorMessagePntr;
ErrorMessagePntr = GetIntoidsMessage (MSG_INTOIDS_CREDITS);
return ReturnPointer;
}
/******************************************************************************
* Returns TRUE if the character is white space. Uses Locale language library
* if available, otherwise just hack it (avoid ctype array).
*/
BOOL LOCALFUNC MyIsSpace (unsigned long character)
{
if (CurrentLocale != NULL)
return IsSpace (CurrentLocale, character);
return (BOOL) (character == ' ' || character == 9 /* tab char */);
}
/****** intoids.library/SignOfIntoid ****************************************
*
* NAME
* SignOfIntoid -- Returns -1, 0 or +1 depending on Intoid's sign.
*
* SYNOPSIS
* MySign = SignOfIntoid( IntegerA )
* D0 D0
*
* LONG SignOfIntoid( Intoid );
*
* FUNCTION
* Fast way of finding the sign of any Intoid.
*
* INPUTS
* IntegerA - any Intoid you want to find the sign of.
*
* RESULT
* MySign - reflects the sign of IntegerA in a LONG value. Returns -1
* for negative integers (includes negative infinity), 0 for zero
* and non-numbers, 1 for positive integers (and +infinity).
*
* SEE ALSO
* CompareIntoids(), CompareIntoidMagnitudes().
*
*****************************************************************************
*/
LONG LIBFUNC SignOfIntoid (REGD0 Intoid IntegerA)
{
IntRepPointer RepA;
long Sign;
if (IntoidIsSmallInt (IntegerA))
{
Sign = IntoidToSmallInt (IntegerA);
if (Sign < 0)
Sign = -1L;
else if (Sign > 0)
Sign = 1L;
}
else if (IntoidIsIntRep (IntegerA))
{
RepA = (IntoidAsPointer) IntegerA;
if (RepA->currentLength == 0) /* Value is IntRep equivalent to zero. */
Sign = 0;
else
Sign = RepA->positiveSign ? 1L : -1L;
}
else if (IntegerA == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY))
Sign = 1L;
else if (IntegerA == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
Sign = -1L;
else /* Some other special code. */
Sign = 0;
return Sign;
}
/****** intoids.library/IntoidFitsInLong ************************************
*
* NAME
* IntoidFitsInLong -- Tests if an Intoid fits in a long int.
*
* SYNOPSIS
* Boolean = IntoidFitsInLong( IntegerA )
* D0 D0
*
* BOOL IntoidFitsInLong( Intoid );
*
* FUNCTION
* Tests if an Intoid can be represented by an ordinary 32 bit long int.
*
* INPUTS
* IntegerA - any Intoid.
*
* RESULT
* Boolean - TRUE if the Intoid can fit into a long integer. Numbers
* too big, infinities and not-a-number return FALSE.
*
* SEE ALSO
* IntoidToLong().
*
*****************************************************************************
*/
BOOL LIBFUNC IntoidFitsInLong (REGD0 Intoid IntegerA)
{
int i;
IntRepPointer MyIntRep;
BOOL ReturnValue;
if (IntoidIsSmallInt (IntegerA))
ReturnValue = TRUE;
else if (IntoidIsIntRep (IntegerA))
{
MyIntRep = (IntoidAsPointer) IntegerA;
if (MyIntRep->currentLength > SHORT_PER_LONG) /* More than 32 bits. */
ReturnValue = FALSE;
else if (MyIntRep->currentLength < SHORT_PER_LONG) /* Number has 16 bits or less. */
ReturnValue = TRUE;
else /* Unsigned value is same size as a long, 32 bits. */
{
if (MyIntRep->positiveSign) /* Positive OK if 0 to 0x7FFFFFFF. */
{
if (MyIntRep->numberArray [SHORT_PER_LONG-1] <= 0x7FFFU)
ReturnValue = TRUE;
else
ReturnValue = FALSE;
}
else /* Negative with 32 bit value. OK if whole is <= 0x80000000 */
{
if (MyIntRep->numberArray [SHORT_PER_LONG-1] <= 0x7FFFU)
ReturnValue = TRUE;
else if (MyIntRep->numberArray [SHORT_PER_LONG-1] == 0x8000U)
{
/* Check for special case value of 0x80000000. */
i = SHORT_PER_LONG - 2;
ReturnValue = TRUE;
while (i >= 0)
{
if (MyIntRep->numberArray [i] != 0)
{
ReturnValue = FALSE; /* Nope, value is too big to fit. */
break;
}
--i;
}
}
else /* Negative value starting with 0x8001 or larger. Too big. */
ReturnValue = FALSE;
}
}
}
else
ReturnValue = FALSE; /* A special code, can't be represented. */
return ReturnValue;
}
/****** intoids.library/IntoidToLong ****************************************
*
* NAME
* IntoidToLong -- Converts an Intoid into a long integer.
*
* SYNOPSIS
* MyLong = IntoidToLong( IntegerA )
* D0 D0
*
* LONG IntoidToLong( Intoid );
*
* FUNCTION
* Converts an Intoid into the closest long integer.
*
* INPUTS
* IntegerA - any Intoid.
*
* RESULT
* MyLong - an ordinary 32 bit long int equal to IntegerA in value, if
* possible. If IntegerA is too big or too small then you get the
* largest (0x7FFFFFFF) or smallest (0x80000000) long possible.
* Plus and minus infinity become largest and smallest longs
* respectively. Not-a-number becomes zero.
*
* SEE ALSO
* IntoidFitsInLong().
*
*****************************************************************************
*/
LONG LIBFUNC IntoidToLong (REGD0 Intoid IntegerA)
{
int i;
IntRepPointer MyIntRep;
unsigned long NewLong;
long ReturnValue;
if (IntoidIsSmallInt (IntegerA))
ReturnValue = IntoidToSmallInt (IntegerA);
else if (IntoidIsIntRep (IntegerA))
{
MyIntRep = (IntoidAsPointer) IntegerA;
if (MyIntRep->currentLength > SHORT_PER_LONG) /* More than 32 bits. */
{
if (MyIntRep->positiveSign)
ReturnValue = LONG_MAX;
else
ReturnValue = LONG_MIN;
}
else if (MyIntRep->currentLength == 0)
ReturnValue = 0;
else if (MyIntRep->currentLength <= SHORT_PER_LONG - 1)
{
if (SHORT_PER_LONG > 2)
{
NewLong = 0;
for (i = MyIntRep->currentLength - 1; i >= 0; i--)
NewLong = up (NewLong) | MyIntRep->numberArray[i];
}
else /* 32 bit longs, know there is just 1 short in the number. */
NewLong = MyIntRep->numberArray[0];
if (MyIntRep->positiveSign)
ReturnValue = (long) NewLong;
else
ReturnValue = -(long) NewLong;
}
else /* Value is same length as a long, may or may not overflow. */
{
/* Length is 2 shorts, 32 bits, may overflow since we have an unsigned 32
bit value stored in numberArray and we are converting to a signed
value. */
if (SHORT_PER_LONG > 2)
{
NewLong = 0;
for (i = SHORT_PER_LONG - 1; i >= 0; i--)
NewLong = up (NewLong) | MyIntRep->numberArray[i];
}
else /* 32 bit longs, know there are 2 shorts in the number. */
NewLong = up (MyIntRep->numberArray [1]) | MyIntRep->numberArray [0];
if (MyIntRep->positiveSign) /* Positive OK if 0 to 0x7FFFFFFF. */
{
ReturnValue = (long) NewLong;
if (ReturnValue < 0) /* Too big, so it became negative. */
ReturnValue = LONG_MAX; /* Use largest positive long instead. */
}
else /* Negative OK if 0 to 0x80000000. */
{
ReturnValue = -(long) NewLong;
if (ReturnValue >= 0) /* Overflowed, and became positive. */
ReturnValue = LONG_MIN; /* Use smallest long integer instead. */
}
}
}
else /* Must be a special code. */
{
switch (IntoidToSpecialCode (IntegerA))
{
case ISC_POSITIVE_INFINITY:
ReturnValue = LONG_MAX;
break;
case ISC_NEGATIVE_INFINITY:
ReturnValue = LONG_MIN;
break;
default:
ReturnValue = 0;
break;
}
}
return ReturnValue;
}
/******************************************************************************
* Converts a long integer into an IntRep.
*/
IntRepPointer LOCALFUNC LongToIntRep (const long LongA, Intoid RecycleMe)
{
int i;
IntRepPointer MyIntRep;
Intoid NewIntoid;
unsigned long TempLong;
/* Ok, have to allocate an IntRep type representation, with at least 32 bits
(2 shorts) of storage. ResizeIntoid sets it's length to 2 shorts and
sets the rest of the contents to zero, except the first 2 shorts. */
NewIntoid = ResizeIntoid (RecycleMe, SHORT_PER_LONG,
FALSE /* Don't need to zero it */);
if (NewIntoid == NULL) return NULL;
MyIntRep = (IntoidAsPointer) NewIntoid;
if (LongA < 0)
{
MyIntRep->positiveSign = I_NEGATIVE;
TempLong = -LongA; /* Should work for even 0x80000000. */
}
else /* A positive or zero number. */
{
MyIntRep->positiveSign = I_POSITIVE;
TempLong = LongA;
}
if (SHORT_PER_LONG > 2)
{
for (i = 0; i < SHORT_PER_LONG; i++)
{
MyIntRep->numberArray[i] = extract (TempLong);
TempLong = down (TempLong);
}
}
else /* SHORT_PER_LONG == 2 */
{
MyIntRep->numberArray[0] = extract (TempLong);
MyIntRep->numberArray[1] = extract (down (TempLong));
}
Icheck (MyIntRep); /* Reduce currentLength to remove leading zeroes. */
return NewIntoid;
}
/****** intoids.library/LongToIntoid ****************************************
*
* NAME
* LongToIntoid -- Converts a long int to an Intoid.
*
* SYNOPSIS
* NewIntoid = LongToIntoid( LongA, RecycleMe )
* D0 D0 A0
*
* Intoid LongToIntoid( LONG, Intoid );
*
* FUNCTION
* Converts a 32 bit long int into an Intoid.
*
* INPUTS
* LongA - the integer you want to convert.
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* NewIntoid - a newly allocated Intoid with value equal to LongA. NULL
* if out of memory.
*
* SEE ALSO
* SmallIntToIntoid().
*
*****************************************************************************
*/
Intoid LIBFUNC LongToIntoid (REGD0 LONG LongA, REGA0 Intoid RecycleMe)
{
/* If it fits in 31 bits, we can fit it into an Intoid as a small integer. */
if ((unsigned long) (LONG_MIN >> 1) <= (unsigned long) LongA ||
(unsigned long) LongA <= (unsigned long) (LONG_MAX >> 1))
{
FreeIntoid (RecycleMe);
return SmallIntToIntoid (LongA);
}
return LongToIntRep (LongA, RecycleMe);
}
/******************************************************************************
* Reduces the Intoid to the smallest representation technique that can hold
* the number. Returns the new Intoid, which may be different (the old
* one may be deleted so don't have any other references to it).
*/
Intoid LOCALFUNC NormalizeIntoid (Intoid IntegerA)
{
IntRepPointer Rep;
if (IntoidIsSpecialCode (IntegerA)) /* Includes NULL case. */
return IntegerA;
if (IntoidIsSmallInt (IntegerA))
return IntegerA;
/* Must be an IntRep. See if it fits in a small integer. */
Rep = (IntoidAsPointer) IntegerA;
if (Rep->currentLength > SHORT_PER_LONG)
return IntegerA; /* Definitely far too big to fit in 31 bits. */
/* If the number might fit in 31 bits, including sign.
Definitely fits in 32 bits, so convert to long and back
(this also frees the IntRep if it isn't needed). */
if (Rep->currentLength < SHORT_PER_LONG ||
Rep->numberArray[SHORT_PER_LONG - 1] <= 0x4000U)
return LongToIntoid (IntoidToLong (IntegerA), IntegerA);
return IntegerA;
}
/****** intoids.library/--FileFormat-- **************************************
*
* NAME
* AGMSPortableIntFormat -- Description of the Portable Integer Format.
*
* FUNCTION
* This binary number format has to handle all values that could
* be represented by an Intoid and has to be efficient in storage
* space (so that the AGMS virtual file system databases aren't
* too large). It also has to be machine independant.
*
* To make things simpler for current computer architectures, it is
* based on a variable quantity of 8 bit bytes (almost all computers can
* pick out an 8 bit byte from their memory efficiently, few can handle
* arbitrarily large bit aligned data nicely, particularly in the C
* language).
*
* The general format is: FirstByte, optional size extension bytes,
* optional data bytes.
*
* The size extension byte(s) are located just after FirstByte and just
* before the actual data bytes. If the first size extension byte is
* 255 (all 1 bits) then the size value is in two bytes after that
* (least significant byte comes first), and if those are all 1 bits
* then the size is in the following 4 bytes, and so on, doubling in
* length if the previous size extension value is all 1 bits. Don't
* use an extension of zero, that can cause trouble with some code
* (extensions of zero are used to signal errors in the Intoids code).
*
* The number of optional data bytes is defined by the FirstByte and
* the optional size extension bytes. Large integers are stored as a
* positive magnitude (the sign is encoded in FirstByte) in the data
* bytes. The least significant byte comes first, followed by
* increasingly more significant bytes. This was done because it is
* easier to shrink or expand from the end of an array rather than the
* start of an array when a number gets smaller or larger.
*
* The first byte can either be a small integer value (so that things
* like file name string lengths fit in a byte) or part of a larger
* integer or a special code. Some numbers can be represented in
* several different ways, you should be able to read all varieties,
* and preferably write the shortest one. Here is the encoding for the
* first byte:
*
* $0 to $7F:
* A small positive integer with value 0 to 127 directly
* corresponding to treating FirstByte as an 8 bit 2's complement
* integer.
*
* $96 to $FF:
* A small negative integer with value -106 to -1 directly
* corresponding to treating FirstByte as an 8 bit 2's complement
* integer.
*
* $80 to $87
* A positive number with 1 to 8 bytes of data following the FirstByte.
* The number of data bytes is (unsigned byte) FirstByte - $7F. No
* size extension bytes.
*
* $88 to $8F
* A negative number with 1 to 8 bytes of data following the FirstByte.
* The number of data bytes is (unsigned byte) FirstByte - $87. No
* size extension bytes.
*
* $90
* A positive integer. Has a size extension and a data section. The
* size extension specifies the number of bytes in the data section.
*
* $91
* A negative integer. Has a size extension and a data section. The
* size extension specifies the number of bytes in the data section.
*
* $92
* Signals that an extended special code follows, the size extension
* bytes specify the special code. There is no data section. Note
* that we want to avoid a size extension of zero (causes trouble),
* so the special codes start at 1.
*
* $93 to $95
* A special code number. The particular one is FirstByte - $92.
* There is no data section or size extension. The special codes are:
* 1: Positive integer infinity.
* 2: Negative integer infinity.
* 3: Not a number. The NULL pointer of numbers. The quiet kind
* of not-a-number (doesn't raise error conditions when used).
* 4: Noisy variety of not-a-number. Generates an error if used
* in math? Intoids don't support it. Uses FirstByte $92
* style encoding.
* 5+: Future use, uses FirstByte $92 style for encoding.
*
*
* EXAMPLE
* 00
* A positive integer with a value of zero. No size extension or
* data bytes.
*
* F6
* A negative integer with a value of -10. No size extension or
* data bytes.
*
* 80 C8
* A positive integer with no size extension and 1 ($80 - $7F = 1)
* data byte. It has a value of +200.
*
* 8B 00 CA 9A 3B
* A negative integer with no size extension and 4 ($8B - $87 = 4)
* data bytes. It has a value of -$3B9ACA00 = -1000000000.
*
* 90 12 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18
* A positive integer with a size extension of $12 = 18, and 18 data
* bytes. It has a value of $181716151413121110090807060504030201.
*
* 90 FF 23 01 (FF repeated 291 times)
* A positive integer with a size extension of $123 = 291 (note the
* first size extension byte is $FF which means see the next two
* bytes ($23 $01) for the size extension). The data section has
* 291 bytes of $FF each. This is an integer with a value of
* 2^2328 - 1.
*
* 93
* Special code 1, meaning positive infinity.
*
* 94
* Special code 2, meaning negative infinity.
*
* 95
* This is special code 3, the code for a quiet not-a-number.
*
* 92 04
* Special code number 4, error signalling variety of not-a-number.
*
* 92 FF 23 01
* Special code number $123 = 291. Currently not defined to mean
* anything (future use). No data section even though it has size
* extension bytes.
*
* 91 FF FF FF 78 56 34 12 01 02 03 [and 305419893 more bytes here]
* This is a negative integer with $12345678 = 305419896 bytes of
* number in the data section. Note the way the size extension grew
* to hold the size of $12345678. Most current computers would have
* trouble loading this 300 megabyte number into memory, let alone
* multiplying it with another one.
*
*****************************************************************************
*/
/****** intoids.library/--UserCallBackStreamFunction-- **********************
*
* NAME
* AGMSPortableIntStreamCallBack -- User defined function for stream IO.
*
* SYNOPSIS
* LONG STACKCALL AGMSPortableIntStreamCallBack (
* ULONG Operation, APTR Buffer, LONG Amount, APTR UserPntr );
*
* FUNCTION
* A user defined callback function that is used for reading and writing
* AGMS Portable Integer numbers from or to a stream.
*
* INPUTS
* Operation - this argument specifies what your function should do:
* 0: Read data. Fill the buffer with Amount data bytes. Your
* function returns the amount read, which will be less than
* the Amount requested if it fails (end of file etc).
* 1: Seek. Seek relative to the current stream postion by
* Amount bytes. Amount is negative for seeking backwards,
* but PortableInt functions will never do that. Returns
* the position in the stream if known, or just zero when
* it succeeds, returns -1 if it fails (seek past EOF or
* before start of file, IO errors). Return -2 if you don't
* implement seek, and a bunch of reads will be used instead.
* 2: Write data. Write Amount bytes from the buffer to the
* stream. Returns the amount written, which will be less
* than the Amount requested if it fails (IO error etc).
* N: For other unsupported Operation values please return -2.
* Buffer - points to a buffer where the read in data is saved or
* the data to be written is obtained.
* Amount - used to specify the number of bytes to
* read, skip over, or write.
* UserPntr - the same value as passed into the original
* PortableInt function call. Usually will be a file
* handle or something similar.
*
* RESULT
* ActualAmount - the function returns the number of bytes actually
* read or written, or the absolute file position after a seek.
*
* NOTES
* The callback function is called using the C language stack
* based calling convention. In SAS C this is specified with
* the __stdargs keywords.
*
* BUGS
* Returned seek position value only good for files up to 2G.
*
* SEE ALSO
* PortIntCallBackOp enum in Intoids.h, STACKCALL macro in Intoids.h.
*
*****************************************************************************
*/
/******************************************************************************
* Read the multibyte extension number from the stream. Returns 0 if
* something goes wrong. The extension is initially 1 byte long, if that is
* all 1 bits (value 0xFF) then read the next two bytes. If they are all
* 1's then read the next 4 bytes, and so on, doubling the number of bytes
* read. Fails if the extension value is too big for a long integer. Also
* returns the number of bytes actually read, if ByteCountPntr isn't NULL.
*/
ULONG LOCALFUNC ReadExtensionValue (PortIntCallBackPntr CallBack,
APTR UserPntr, ULONG *ByteCountPntr)
{
LONG ActualIOResult;
UBYTE ByteArray [CHAR_PER_LONG];
ULONG ByteCount;
ULONG ExtensionFullCode;
ULONG ExtensionValue;
int i;
LONG ReadSize;
ByteCount = 0;
ReadSize = 1;
ExtensionValue = ExtensionFullCode = 0;
while (ExtensionValue == ExtensionFullCode)
{
if (ReadSize > CHAR_PER_LONG)
{
/* Extension is bigger than what fits in a long. */
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_BAD_FORMAT));
ExtensionValue = 0; /* Signal an error. */
break;
}
/* Read in the next extension value's bytes. */
ActualIOResult = (* CallBack) (PICBOP_READ, ByteArray, ReadSize, UserPntr);
ByteCount += ActualIOResult;
if (ActualIOResult != ReadSize)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_READ_ERROR));
ExtensionValue = 0; /* Signal an error. */
break;
}
/* Convert from little endian to native format. */
ExtensionValue = 0;
for (i = ReadSize - 1; i >= 0 ; --i)
{
ExtensionValue <<= 8;
ExtensionValue |= ByteArray[i];
}
/* Generate all 1 bits in ReadSize bytes for
the current ExtensionFullCode. */
ExtensionFullCode = (1UL << (8 * ReadSize)) - 1UL;
/* Double the size for next read attempt. */
ReadSize += ReadSize;
}
/* Return the results. */
if (ByteCountPntr != NULL)
*ByteCountPntr = ByteCount;
return ExtensionValue;
}
/******************************************************************************
* Read the header information from an AGMS Portable Integer. Returns
* optional number of bytes read, kind of number, auxiliary value (either
* the small integer or special code or number of bytes in a large integer),
* and a success flag (TRUE for success, FALSE for error). Fails on IO
* errors and size extension field being too big to fit in a long.
*/
typedef enum NumberTypeEnum
{
PI_SPECIAL_CODE,
PI_SMALL_INTEGER,
PI_LARGE_POSITIVE_INTEGER,
PI_LARGE_NEGATIVE_INTEGER,
} NumberType;
BOOL LOCALFUNC ReadPortableIntHeader (PortIntCallBackPntr CallBack,
APTR UserPntr, ULONG *ByteCountPntr, NumberType *NumberKindPntr,
ULONG *AuxiliaryDataPntr)
{
LONG ActualIOResult;
ULONG AuxiliaryData;
ULONG ByteCount;
UBYTE FirstByte;
NumberType NumberKind;
BOOL ReturnCode;
UBYTE UByteOnStack;
ULONG ULongOnStack;
ReturnCode = FALSE; /* Default failure. */
ByteCount = AuxiliaryData = NumberKind = 0;
if (CallBack == NULL)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_BAD_INPUT_PARMS));
goto FunctionExit;
}
/* Get the FirstByte of the number. */
ActualIOResult = (* CallBack) (PICBOP_READ, &UByteOnStack, 1, UserPntr);
FirstByte = UByteOnStack;
ByteCount += ActualIOResult;
if (ActualIOResult != 1)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_READ_ERROR));
goto FunctionExit;
}
/* Decode the NumberKind and read size extension if needed. */
if (FirstByte <= 0x7FU) /* A positive small integer. */
{
NumberKind = PI_SMALL_INTEGER;
AuxiliaryData = FirstByte;
}
else if (FirstByte >= 0x96U) /* A negative small integer. */
{
NumberKind = PI_SMALL_INTEGER;
AuxiliaryData = (long) (signed char) FirstByte; /* Sign extend it. */
}
else if (FirstByte <= 0x87U) /* A positive large integer, $80 to $87. */
{
NumberKind = PI_LARGE_POSITIVE_INTEGER;
AuxiliaryData = FirstByte - 0x7FU; /* Length in bytes. */
}
else if (FirstByte <= 0x8FU) /* A negative large integer, $88 to $8F. */
{
NumberKind = PI_LARGE_NEGATIVE_INTEGER;
AuxiliaryData = FirstByte - 0x87U; /* Length in bytes. */
}
else if (FirstByte == 0x90U) /* A positive large integer, variable size. */
{
NumberKind = PI_LARGE_POSITIVE_INTEGER;
AuxiliaryData = ReadExtensionValue (CallBack, UserPntr, &ULongOnStack);
ByteCount += ULongOnStack;
if (AuxiliaryData == 0)
goto FunctionExit; /* Error while reading size extension. */
}
else if (FirstByte == 0x91U) /* A negative large integer, variable size. */
{
NumberKind = PI_LARGE_NEGATIVE_INTEGER;
AuxiliaryData = ReadExtensionValue (CallBack, UserPntr, &ULongOnStack);
ByteCount += ULongOnStack;
if (AuxiliaryData == 0)
goto FunctionExit; /* Error while reading size extension. */
}
else if (FirstByte == 0x92U) /* A special code, size is code's value. */
{
NumberKind = PI_SPECIAL_CODE;
AuxiliaryData = ReadExtensionValue (CallBack, UserPntr, &ULongOnStack);
ByteCount += ULongOnStack;
if (AuxiliaryData == 0)
goto FunctionExit; /* Error while reading size extension. */
}
else /* $93 to $95, a special code. */
{
NumberKind = PI_SPECIAL_CODE;
AuxiliaryData = FirstByte - 0x92U;
}
ReturnCode = TRUE; /* Success at last. */
FunctionExit:
/* Stuff the return values into their memory locations. */
if (ByteCountPntr != NULL)
*ByteCountPntr = ByteCount;
*NumberKindPntr = NumberKind;
*AuxiliaryDataPntr = AuxiliaryData;
return ReturnCode;
}
/******************************************************************************
* This callback function converts a buffer into a stream. In other words, it
* handles reading, writing and seeking. The end of the buffer corresponds to
* the end of the stream. See PortIntCallBackPntr in Intoids.h for details.
*/
typedef struct BufferStreamStruct
{
UBYTE *start; /* Points to start of user's buffer. */
UBYTE *end; /* Points just past end of user's buffer. */
UBYTE *current; /* Current position in user's buffer. */
} BufferStreamRecord, *BufferStreamPointer;
LONG STACKCALL BufferAsStreamCallBack (ULONG Operation, APTR Buffer,
LONG Amount, APTR UserPntr)
{
LONG ActualAmount;
BufferStreamPointer UsersBufferInfo;
UsersBufferInfo = UserPntr;
switch (Operation)
{
case PICBOP_READ:
ActualAmount = UsersBufferInfo->end - UsersBufferInfo->current;
if (ActualAmount > Amount)
ActualAmount = Amount;
if (ActualAmount > 0)
{
memcpy (Buffer, UsersBufferInfo->current, (size_t) ActualAmount);
UsersBufferInfo->current += ActualAmount;
}
else /* Zero or negative amount to copy. Do nothing. */
ActualAmount = 0;
return ActualAmount;
case PICBOP_SEEK:
if (Amount >= 0)
{
ActualAmount = UsersBufferInfo->end - UsersBufferInfo->current;
if (ActualAmount >= Amount) /* Enough space to do the seek. */
{
UsersBufferInfo->current += Amount;
return UsersBufferInfo->current - UsersBufferInfo->start;
}
return -1; /* Can't seek that far. */
}
/* Seeking backwards. */
ActualAmount = UsersBufferInfo->start - UsersBufferInfo->current;
if (ActualAmount <= Amount) /* Enough space to do the seek. */
{
UsersBufferInfo->current += Amount;
return UsersBufferInfo->current - UsersBufferInfo->start;
}
return -1; /* Can't seek that far. */
case PICBOP_WRITE:
ActualAmount = UsersBufferInfo->end - UsersBufferInfo->current;
if (ActualAmount > Amount)
ActualAmount = Amount;
if (ActualAmount > 0)
{
memcpy (UsersBufferInfo->current, Buffer, (size_t) ActualAmount);
UsersBufferInfo->current += ActualAmount;
}
else /* Zero or negative amount to copy. Do nothing. */
ActualAmount = 0;
return ActualAmount;
}
return -2; /* Unimplemented operation. */
}
/****** intoids.library/PortableIntLengthViaCallBack ************************
*
* NAME
* PortableIntLengthViaCallBack -- Gets byte size of portable integer.
*
* SYNOPSIS
* Length = PortableIntLengthViaCallBack( CallBack, UserPntr )
* D0 D0 D1
*
* ULONG PortableIntLengthViaCallBack( PortIntCallBackPntr, APTR );
*
* FUNCTION
* Finds the length in bytes of an AGMS Portable Integer and skips
* over it.
*
* INPUTS
* CallBack - a user provided function that handles the input
* stream, see the intoids.library/AGMSPortableIntStreamCallBack
* entry in these autodocs for details.
* UserPntr - Any pointer sized value you want. This value will be
* passed to your callback function. Typically used for file
* handles.
*
* RESULT
* Length - returns the length or zero if something went wrong (read
* error or unsupported number kind or bad number format, in which
* case an unknown number of bytes will have been read). Length
* includes the header size.
*
* NOTES
* As a side effect, it has read all the bytes in the number. You can
* use this function to skip over a number (it seeks past the number
* contents for big numbers).
*
* BUGS
* Doesn't work for numbers that take more than 2 Gigabytes of storage
* space (garbage results in that case, and unpredictable number of
* bytes read in that case too - but then that would be a very big
* number that nobody would ever use, well except when trying to foil
* nanotechnology based decryption systems :-).
*
* SEE ALSO
* PortableIntLengthViaBuffer(), AGMSPortableIntStreamCallBack.
*
*****************************************************************************
*/
ULONG LIBFUNC PortableIntLengthViaCallBack (
REGD0 PortIntCallBackPntr CallBack,
REGD1 APTR UserPntr)
{
LONG ActualIOResult;
ULONG Amount;
ULONG ByteCount;
ULONG DataSize;
ULONG NumberKind;
char TempBuffer [20];
if (!ReadPortableIntHeader (CallBack, UserPntr,
&ByteCount, &NumberKind, &DataSize))
return 0; /* Some sort of error. */
/* Skip over the actual data bytes, if any. */
if (NumberKind == PI_LARGE_POSITIVE_INTEGER ||
NumberKind == PI_LARGE_NEGATIVE_INTEGER)
{
ActualIOResult = (* CallBack) (PICBOP_SEEK, NULL, DataSize, UserPntr);
if (ActualIOResult == -1 /* Seek returns -1 for errors. */)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_SEEK_ERROR));
return 0;
}
if (ActualIOResult == -2 /* Seek returns -2 for unimplemented. */)
{
/* Simulate a seek by doing a bunch of reads. */
while (DataSize > 0)
{
if (DataSize > sizeof (TempBuffer))
Amount = sizeof (TempBuffer);
else
Amount = DataSize;
ActualIOResult = (* CallBack) (PICBOP_READ,
TempBuffer, Amount, UserPntr);
ByteCount += ActualIOResult;
DataSize -= ActualIOResult;
if (ActualIOResult != Amount)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_READ_ERROR));
return 0;
}
}
}
else /* Successful seek. */
ByteCount += DataSize;
}
return ByteCount;
}
/****** intoids.library/PortableIntLengthViaBuffer **************************
*
* NAME
* PortableIntLengthViaBuffer -- Gets byte size of portable integer.
*
* SYNOPSIS
* Length = PortableIntLengthViaBuffer( Buffer, BufferSize )
* D0 A0 D1
*
* ULONG PortableIntLengthViaBuffer( APTR, ULONG );
*
* FUNCTION
* Finds the length in bytes of an AGMS Portable Integer in a buffer.
*
* INPUTS
* Buffer - points to an area of memory containing the portable integer.
* BufferSize - amount of data in the buffer.
*
* RESULT
* Length - returns the length or zero if something went wrong (hit end
* of buffer before number completed, unsupported number kind or bad
* number format). Length includes the header size.
*
* BUGS
* Doesn't work for numbers that take more than 2 Gigabytes of storage
* space (garbage results in that case - but then that would be a very
* big number that nobody would ever use, well maybe we could return an
* Intoid instead of a ULONG :-).
*
* SEE ALSO
* PortableIntLengthViaCallBack().
*
*****************************************************************************
*/
ULONG LIBFUNC PortableIntLengthViaBuffer (
REGA0 APTR Buffer,
REGD1 ULONG BufferSize)
{
BufferStreamRecord BufferAsStream;
BufferAsStream.current = BufferAsStream.start = Buffer;
BufferAsStream.end = (UBYTE *) Buffer + BufferSize;
return PortableIntLengthViaCallBack (BufferAsStreamCallBack,
&BufferAsStream);
}
/****** intoids.library/PortableIntToIntoidViaCallBack **********************
*
* NAME
* PortableIntToIntoidViaCallBack -- Converts portable int to Intoid.
*
* SYNOPSIS
* NewIntoid = PortableIntToIntoidViaCallBack
* ( CallBack, UserPntr, AmountReadPntr, RecycleMe )
* D0 A0 D0 A1 D1
*
* Intoid PortableIntToIntoidViaCallBack
* ( PortIntCallBackPntr, APTR, ULONG *, Intoid );
*
* FUNCTION
* Reads an arbitrarily long integer in AGMS Portable Integer format
* from some input stream and returns the result as an Intoid.
*
* INPUTS
* CallBack - a user provided function that handles the input
* stream, see the intoids.library/AGMSPortableIntStreamCallBack
* entry in these autodocs for details.
* UserPntr - Any pointer sized value you want. This value will be
* passed to your callback function. Typically used for file
* handles.
* AmountReadPntr - points to a long variable where the number of bytes
* read will be placed. Set to NULL if you don't want to use this
* feature.
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* NewIntoid - an Intoid equivalent to the number, or NULL if it fails,
* also NULL if the number is not-a-number.
* AmountReadPntr - pointed to variable is updated with the number of
* bytes that were actually read.
*
* NOTES
* Advances the stream to just past the number, if it succeded.
*
* BUGS
* Doesn't work with numbers over 2G in storage space, but they won't fit
* in an Intoid anyways (128K bytes max in an Intoid).
*
* SEE ALSO
* PortableIntToIntoidViaBuffer(), AGMSPortableIntStreamCallBack.
*
*****************************************************************************
*/
Intoid LIBFUNC PortableIntToIntoidViaCallBack (
REGA0 PortIntCallBackPntr CallBack,
REGD0 APTR UserPntr,
REGA1 ULONG *AmountReadPntr,
REGD1 Intoid RecycleMe)
{
ULONG AuxiliaryData;
LONG ActualIOResult;
ULONG ByteCount;
Intoid NewIntoid;
IntRepPointer NewIntRep;
NumberType NumberKind;
unsigned short *ShortPntr;
ULONG ShortSize;
BOOL Success;
unsigned short TempShort;
ULONG ULongOnStack;
NewIntoid = NULL; /* Default return value for error conditions. */
ByteCount = 0;
Success = ReadPortableIntHeader (CallBack, UserPntr, &ULongOnStack,
&NumberKind, &AuxiliaryData);
ByteCount += ULongOnStack;
if (!Success) /* Error while reading header? */
{
FreeIntoid (RecycleMe);
goto FunctionExit;
}
switch (NumberKind)
{
case PI_SMALL_INTEGER:
NewIntoid = SmallIntToIntoid (AuxiliaryData);
break;
case PI_LARGE_POSITIVE_INTEGER:
case PI_LARGE_NEGATIVE_INTEGER:
/* Allocate an Intoid large enough to hold the number. */
ShortSize = (AuxiliaryData + sizeof (short) - 1) / sizeof (short);
if (ShortSize <= 0) /* Some sort of bug. */
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_BAD_FORMAT));
FreeIntoid (RecycleMe);
break;
}
NewIntoid = ResizeIntoid (RecycleMe, ShortSize,
FALSE /* don't bother zeroing ShortSize part of it */);
if (NewIntoid == NULL)
break; /* Out of memory. */
NewIntRep = (IntoidAsPointer) NewIntoid;
/* Read in the number's magnitude bytes. */
NewIntRep->numberArray[ShortSize-1] = 0; /* So high byte is zero. */
ActualIOResult = (* CallBack) (PICBOP_READ, NewIntRep->numberArray,
AuxiliaryData, UserPntr);
ByteCount += ActualIOResult;
if (ActualIOResult != AuxiliaryData)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_READ_ERROR));
FreeIntoid (NewIntoid);
NewIntoid = NULL;
break;
}
/* Reverse the bytes in each short integer, if this computer
uses most significant byte first order. */
#if _M68000 /* Motorola 68000 series CPUs uses MSB order. */
ShortPntr = NewIntRep->numberArray + ShortSize;
do
{
TempShort = *--ShortPntr;
*ShortPntr = (TempShort << 8) | (TempShort >> 8);
} while (ShortPntr != NewIntRep->numberArray);
#endif /* _M68000 */
/* Normalise the result so it is a valid Intoid. */
NewIntRep->positiveSign = (NumberKind == PI_LARGE_POSITIVE_INTEGER);
Icheck (NewIntRep);
NewIntoid = NormalizeIntoid (NewIntoid);
break;
case PI_SPECIAL_CODE:
FreeIntoid (RecycleMe);
if (AuxiliaryData == 1)
{
NewIntoid = INTOID_POSITIVE_INFINITY;
break;
}
else if (AuxiliaryData == 2)
{
NewIntoid = INTOID_NEGATIVE_INFINITY;
break;
}
else if (AuxiliaryData == 3 || AuxiliaryData == 4)
{
NewIntoid = NULL; /* Quiet and noisy not-a-number become NULL. */
break;
}
/* Otherwise an unknown special code. */
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_BAD_FORMAT));
break;
default: /* Unknown kind. */
FreeIntoid (RecycleMe);
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_BAD_FORMAT));
break;
}
/* Set various return values and return. */
FunctionExit:
if (AmountReadPntr != NULL)
*AmountReadPntr = ByteCount;
return NewIntoid;
}
/****** intoids.library/PortableIntToIntoidViaBuffer ************************
*
* NAME
* PortableIntToIntoidViaBuffer -- Converts portable int to Intoid.
*
* SYNOPSIS
* NewIntoid = PortableIntToIntoidViaBuffer
* ( Buffer, BufferSize, AmountReadPntr, RecycleMe )
* D0 A0 D0 A1 D1
*
* Intoid PortableIntToIntoidViaBuffer( APTR, ULONG, ULONG *, Intoid );
*
* FUNCTION
* Reads an arbitrarily long integer in AGMS Portable Integer format
* from a buffer and returns the Intoid equivalent.
*
* INPUTS
* Buffer - points to an area of memory containing the portable integer.
* BufferSize - amount of data in the buffer. Has to be at least
* enough for the entire portable integer, or you get NULL returned.
* AmountReadPntr - points to a long variable where the number of bytes
* read will be placed. Set to NULL if you don't want to use this
* feature.
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* NewIntoid - an Intoid equivalent to the number, or NULL if it fails,
* also NULL if the number is not-a-number.
* AmountReadPntr - pointed to variable is updated with the number of
* bytes that were actually read, will be from zero to BufferSize.
*
* BUGS
* Doesn't work with numbers over 2G in storage space, but they won't fit
* in an Intoid anyways (128K bytes max in an Intoid) or even in most
* system's memory.
*
* SEE ALSO
* PortableIntToIntoidViaCallBack().
*
*****************************************************************************
*/
Intoid LIBFUNC PortableIntToIntoidViaBuffer (
REGA0 APTR Buffer,
REGD0 ULONG BufferSize,
REGA1 ULONG *AmountReadPntr,
REGD1 Intoid RecycleMe)
{
BufferStreamRecord BufferAsStream;
BufferAsStream.current = BufferAsStream.start = Buffer;
BufferAsStream.end = (UBYTE *) Buffer + BufferSize;
return PortableIntToIntoidViaCallBack (BufferAsStreamCallBack,
&BufferAsStream, AmountReadPntr, RecycleMe);
}
/****** intoids.library/IntoidToPortableIntViaCallBack **********************
*
* NAME
* IntoidToPortableIntViaCallBack -- Converts Intoid to portable int.
*
* SYNOPSIS
* Success = IntoidToPortableIntViaCallBack
* ( AnIntoid, BytesWrittenPntr, CallBack, UserPntr)
* D0 D0 A0 D1 A1
*
* BOOL IntoidToPortableIntViaCallBack
* ( Intoid, ULONG *, PortIntCallBackPntr, APTR );
*
* FUNCTION
* Writes the AGMS Portable Integer binary format equivalent of the
* Intoid to a stream using a callback function. Also tests to see if
* your compiler can handle 30 letter function names :-).
*
* INPUTS
* AnIntoid - the Intoid big integer to be converted.
* BytesWrittenPntr - points to a ULONG that will be set to the number
* of bytes written, specify NULL if you aren't using this feature.
* CallBack - a user provided function that handles the input
* stream, see the intoids.library/AGMSPortableIntStreamCallBack
* entry in these autodocs for details.
* UserPntr - Any pointer sized value you want. This value will be
* passed to your callback function. Typically used for file
* handles.
*
* RESULT
* Success - returns TRUE (1) if successful, FALSE (0) if it fails.
* BytesWrittenPntr - the pointed to ULONG is set to the number of
* bytes actually written. If you have an IO error then this
* will be less than the actual size (see PortableIntSizeOfIntoid())
* for the number.
*
* SEE ALSO
* PortableIntSizeOfIntoid(), IntoidToPortableIntViaBuffer(),
* AGMSPortableIntStreamCallBack.
*
*****************************************************************************
*/
BOOL LIBFUNC IntoidToPortableIntViaCallBack (
REGD0 Intoid AnIntoid,
REGA0 ULONG *BytesWrittenPntr,
REGD1 PortIntCallBackPntr CallBack,
REGA1 APTR UserPntr)
{
ULONG AmountToWrite;
ULONG AmountWritten;
IntRepPointer AnIntRep;
UBYTE ByteArray[8]; /* Even size, FirstByte + size extension */
ULONG ByteCount;
ULONG BytesNeeded;
unsigned short CurrentShort;
LONG LongValue;
unsigned short *NextShortPntr;
BOOL Negative;
BOOL ReturnCode;
IntoidSpecialCodes SpecialCode;
LONG TempLong;
ByteCount = 0;
ReturnCode = FALSE;
if (IntoidIsSmallInt (AnIntoid))
{
LongValue = IntoidToSmallInt (AnIntoid);
if (-106 <= LongValue && LongValue <= 127)
{
/* This integer can be represented as a single byte. */
ByteArray [0] = (UBYTE) LongValue;
AmountToWrite = 1;
}
else /* Doesn't fit directly in a byte, need a variable length type. */
{
TempLong = (Negative = (LongValue < 0)) ? -LongValue : LongValue;
BytesNeeded = 0;
while (TempLong)
{
ByteArray [BytesNeeded + 1] = (UBYTE) TempLong;
BytesNeeded++;
TempLong >>= 8;
}
ByteArray [0] = (Negative ? 0x87 : 0x7F) + BytesNeeded;
AmountToWrite = BytesNeeded + 1;
}
/* Write the encoded number in ByteArray. */
AmountWritten = (* CallBack) (PICBOP_WRITE, ByteArray, AmountToWrite,
UserPntr);
ByteCount += AmountWritten;
if (AmountWritten != AmountToWrite)
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_WRITE_ERROR));
else /* Success */
ReturnCode = TRUE;
}
else if (IntoidIsIntRep (AnIntoid))
{
AnIntRep = (IntoidAsPointer) AnIntoid;
if (AnIntRep->currentLength <= 0) /* Shouldn't happen, but... */
{
ByteArray [0] = 0; /* Code for small integer zero. */
AmountToWrite = 1; /* Size of header to write. */
BytesNeeded = 0; /* Number of body data bytes to write. */
}
else /* Have some data to write. How much? */
{
BytesNeeded = AnIntRep->currentLength * 2;
if (AnIntRep->numberArray [AnIntRep->currentLength - 1] < 256)
--BytesNeeded; /* High byte of high word is zero. */
if (BytesNeeded <= 8) /* Fits in 1 to 8 bytes special code? */
{
ByteArray [0] = (AnIntRep->positiveSign ? 0x7F : 0x87) + BytesNeeded;
AmountToWrite = 1;
}
else /* Have lots of bytes to write, need size extension. */
{
ByteArray [0] = (AnIntRep->positiveSign ? 0x90 : 0x91);
if (BytesNeeded < 0xFF) /* Fits in a byte? 0xFF used for extension. */
{
ByteArray [1] = (UBYTE) BytesNeeded;
AmountToWrite = 2;
}
else if (BytesNeeded < 0xFFFF) /* Fits in 2 bytes? */
{
ByteArray [1] = 0xFF; /* Marks doubling of size extension. */
ByteArray [2] = (UBYTE) BytesNeeded;
ByteArray [3] = (UBYTE) (BytesNeeded >> 8);
AmountToWrite = 4;
}
else /* Has to fit in 4 bytes. */
{
ByteArray [1] = 0xFF; /* Marks doubling of size extension. */
ByteArray [2] = 0xFF; /* Marks redoubling of size extension. */
ByteArray [3] = 0xFF;
ByteArray [4] = (UBYTE) (BytesNeeded);
ByteArray [5] = (UBYTE) (BytesNeeded >> 8);
ByteArray [6] = (UBYTE) (BytesNeeded >> 16);
ByteArray [7] = (UBYTE) (BytesNeeded >> 24);
AmountToWrite = 8;
}
}
}
/* Write the header fields for the IntRep based number. */
AmountWritten = (* CallBack) (PICBOP_WRITE,
ByteArray, AmountToWrite, UserPntr);
ByteCount += AmountWritten;
if (AmountWritten != AmountToWrite)
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_WRITE_ERROR));
else /* Write the body, the actual data bytes. */
{
ReturnCode = TRUE; /* Assume success, errors now clear return code. */
#if _M68000 /* Motorola 68000 series CPUs uses MSB order. */
/* Write out the body, swapping low and high bytes in each short.
Use the ByteArray to buffer things a bit (assumes even size). */
AmountToWrite = 0;
NextShortPntr = AnIntRep->numberArray;
while (BytesNeeded > 0)
{
CurrentShort = *NextShortPntr++;
ByteArray [AmountToWrite++] = (UBYTE) CurrentShort;
if (--BytesNeeded > 0)
{
ByteArray [AmountToWrite++] = (UBYTE) (CurrentShort >> 8);
--BytesNeeded;
}
if (AmountToWrite >= sizeof (ByteArray) || BytesNeeded == 0)
{
AmountWritten = (* CallBack) (PICBOP_WRITE,
ByteArray, AmountToWrite, UserPntr);
ByteCount += AmountWritten;
if (AmountWritten != AmountToWrite)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_WRITE_ERROR));
ReturnCode = FALSE;
break;
}
AmountToWrite = 0;
}
}
#else /* Just dump the data out directly, already in right byte order. */
if ((AmountToWrite = BytesNeeded) > 0)
{
AmountWritten = (* CallBack) (PICBOP_WRITE,
AnIntRep->numberArray, AmountToWrite, UserPntr);
ByteCount += AmountWritten;
if (AmountWritten != AmountToWrite)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_WRITE_ERROR));
ReturnCode = FALSE;
}
}
#endif
}
}
else /* Special code or NULL. */
{
AmountToWrite = 1;
SpecialCode = IntoidToSpecialCode (AnIntoid);
switch (SpecialCode)
{
case ISC_NOT_A_NUMBER:
ByteArray [0] = 0x95;
break;
case ISC_POSITIVE_INFINITY:
ByteArray [0] = 0x93;
break;
case ISC_NEGATIVE_INFINITY:
ByteArray [0] = 0x94;
break;
default:
ByteArray [0] = 0x92; /* Extended special code. */
ByteArray [1] = 4; /* Noisy not-a-number. */
AmountToWrite = 2;
break;
}
AmountWritten = (* CallBack) (PICBOP_WRITE, ByteArray, AmountToWrite,
UserPntr);
ByteCount += AmountWritten;
if (AmountWritten != AmountToWrite)
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_PI_WRITE_ERROR));
else /* Success */
ReturnCode = TRUE;
}
/* Set return values. */
if (BytesWrittenPntr != NULL)
*BytesWrittenPntr = ByteCount;
return ReturnCode;
}
/****** intoids.library/IntoidToPortableIntViaBuffer ************************
*
* NAME
* IntoidToPortableIntViaBuffer -- Converts Intoid to portable int.
*
* SYNOPSIS
* Success = IntoidToPortableIntViaBuffer
* ( AnIntoid, BytesWrittenPntr, Buffer, BufferSize )
* D0 D0 A0 A1 D1
*
* BOOL IntoidToPortableIntViaBuffer( Intoid, ULONG *, APTR, ULONG );
*
* FUNCTION
* Writes the AGMS Portable Integer binary format equivalent of the
* Intoid (big integer) to a buffer.
*
* INPUTS
* AnIntoid - the arbitrarily big integer to be converted.
* BytesWrittenPntr - points to a ULONG that will be set to the number
* of bytes written, specify NULL if you aren't using this feature.
* Buffer - points to an area of memory where the portable integer will
* be written to.
* BufferSize - amount of space in the buffer. Has to be at least
* enough for the entire portable integer, or you get a partial
* portable integer in the buffer and BytesWritten equal to
* BufferSize.
*
* RESULT
* Success - returns TRUE (1) if successful, FALSE (0) if it fails.
* BytesWrittenPntr - the pointed to ULONG is set to the number of
* bytes actually written. If you have an IO error then this
* will be less than the actual size (see PortableIntSizeOfIntoid())
* for the number.
*
* SEE ALSO
* PortableIntSizeOfIntoid(), IntoidToPortableIntViaCallBack().
*
*****************************************************************************
*/
BOOL LIBFUNC IntoidToPortableIntViaBuffer (
REGD0 Intoid AnIntoid,
REGA0 ULONG *BytesWrittenPntr,
REGA1 APTR Buffer,
REGD1 ULONG BufferSize)
{
BufferStreamRecord BufferAsStream;
BufferAsStream.current = BufferAsStream.start = Buffer;
BufferAsStream.end = (UBYTE *) Buffer + BufferSize;
return IntoidToPortableIntViaCallBack (AnIntoid, BytesWrittenPntr,
BufferAsStreamCallBack, &BufferAsStream);
}
/******************************************************************************
* This callback function does nothing, always succeeding in reading or
* writing or seeking. It is used for finding the size of an Intoid as a
* portable integer by writing the portable integer to nowhere.
*/
LONG STACKCALL DoNothingStreamCallBack (ULONG Operation, APTR Buffer,
LONG Amount, APTR UserPntr)
{
switch (Operation)
{
case PICBOP_READ:
return Amount;
case PICBOP_SEEK:
return 0; /* New seek position is always zero. */
case PICBOP_WRITE:
return Amount;
}
return -2; /* Unimplemented operation. */
}
/****** intoids.library/PortableIntSizeOfIntoid *****************************
*
* NAME
* PortableIntSizeOfIntoid -- Find Intoid size in portable int format.
*
* SYNOPSIS
* Length = PortableIntSizeOfIntoid( AnIntoid )
* D0 D0
*
* ULONG PortableIntSizeOfIntoid( Intoid );
*
* FUNCTION
* Finds the byte size of an Intoid when expressed in AGMS Portable
* Integer format.
*
* INPUTS
* AnIntoid - the big integer you want to find the formatted size of.
*
* RESULT
* Length - the number of bytes it would take for the AGMS Portable
* Integer binary format equivalent of AnIntoid.
*
*****************************************************************************
*/
ULONG LIBFUNC PortableIntSizeOfIntoid (REGA0 Intoid AnIntoid)
{
ULONG ByteCount;
IntoidToPortableIntViaCallBack (AnIntoid, &ByteCount,
DoNothingStreamCallBack, NULL);
return ByteCount;
}
/****** intoids.library/IntoidToAscii ***************************************
*
* NAME
* IntoidToAscii -- Converts an Intoid into printable ASCII text.
*
* SYNOPSIS
* RequiredLength = IntoidToAscii( IntegerA, Buffer, BufferLength, Base)
* D0 A0 A1 D0 D1
*
* LONG IntoidToAscii( Intoid, STRPTR, LONG, UWORD );
*
* FUNCTION
* Converts the Intoid to a readable string number in the given base.
* Also can output special strings for not-a-number and infinities, and
* various error messages if something goes wrong during the conversion.
*
* INPUTS
* IntegerA - any Intoid.
* Buffer - points to a user provided buffer that will be filled with
* the NUL terminated string ASCII text equivalent of IntegerA.
* This variable can be NULL (useful if you just want an estimate of
* how long the string would be).
* BufferLength - how many bytes are in Buffer. Can be zero.
* Base - what number system to use for the conversion. Can be from 2
* to 36 (because it uses the 26 lower case letters and '0' to '9'
* for the output digits).
*
* RESULT
* RequiredLength - returns the number of characters plus one (for the
* NUL at the end of the string) required to represent the number
* (may be more than BufferLength).
* Buffer - filled with the number, or with '*' characters if it isn't
* big enough to hold the number.
*
* NOTES
* Uses about 1K of the caller's stack space.
*
* BUGS
* Numbers can only be converted if they are less than
* MAX_INTOID_ASCII_DIGITS long. This is because a reversed number is
* built up in a buffer this big on the caller's stack (thus you need
* about 1K of stack when calling this function). If a number is too
* big, you will get an error message instead of the number.
*
*****************************************************************************
*/
LONG LIBFUNC IntoidToAscii (REGA0 Intoid IntegerA, REGA1 STRPTR Buffer,
REGD0 LONG BufferLength, REGD1 UWORD Base)
{
char *NextDigitPntr;
BOOL Positive;
long RequiredLength;
char ReversedNumber [MAX_INTOID_ASCII_DIGITS];
char *EndOfReversedNumber;
if (Base < 2 || Base > 36)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_BAD_INPUT_PARMS));
return 0; /* Avoid infinite loops and divide by zero and bad characters. */
}
if (Buffer == NULL)
BufferLength = 0; /* Fine, we will just tell them how many digits. */
NextDigitPntr = ReversedNumber;
EndOfReversedNumber = ReversedNumber + sizeof (ReversedNumber);
Positive = TRUE;
if (IntoidIsSmallInt (IntegerA))
{
unsigned long rem;
long SmallInt = IntoidToSmallInt (IntegerA);
if (SmallInt < 0)
{
SmallInt = -SmallInt;
Positive = FALSE;
}
rem = SmallInt;
while (rem != 0)
{
char ch;
#if _AMIGA && __SASC
rem = UDivMod32 (rem, (unsigned long) Base);
ch = getreg (REG_D1);
#else
ch = rem % Base;
rem /= Base;
#endif
if (ch >= 10)
ch += 'a' - 10;
else
ch += '0';
*NextDigitPntr++ = ch;
}
}
else if (IntoidIsIntRep (IntegerA))
{
IntRepPointer IntRepA = (IntoidAsPointer) IntegerA;
IntRepPointer TempIntRep;
TempIntRep = Icopy (NULL, IntRepA);
if (TempIntRep == NULL)
{
strcpy (ReversedNumber,
GetIntoidsMessage (MSG_INTOIDS_PRINTING_OUT_OF_MEMORY));
strrev (ReversedNumber);
NextDigitPntr = ReversedNumber + strlen (ReversedNumber);
}
else /* Not out of memory. */
{
/* split division by base into two parts:
first divide by biggest power of base that fits in an unsigned short,
then use straight signed div/mods from there. */
/* find power */
int bpower = 1;
unsigned short b = Base;
unsigned short maxb = I_MAXNUM / Base;
while (b < maxb)
{
b *= Base;
++bpower;
}
Positive = IntRepA->positiveSign;
for(;;)
{
unsigned short rem = unscale(TempIntRep->numberArray,
TempIntRep->currentLength, b, TempIntRep->numberArray);
Icheck(TempIntRep);
if (TempIntRep->currentLength == 0)
{
while (rem != 0)
{
char ch;
unsigned short u;
u = rem / Base;
ch = rem - u * Base;
rem = u;
if (ch >= 10)
ch += 'a' - 10;
else
ch += '0';
if (NextDigitPntr < EndOfReversedNumber)
*NextDigitPntr = ch;
++NextDigitPntr;
}
Idelete (TempIntRep);
break;
}
else
{
int i;
for (i = 0; i < bpower; ++i)
{
char ch;
unsigned short u;
u = rem / Base;
ch = rem - u * Base;
rem = u;
if (ch >= 10)
ch += 'a' - 10;
else
ch += '0';
if (NextDigitPntr < EndOfReversedNumber)
*NextDigitPntr = ch;
++NextDigitPntr;
}
}
}
}
}
else /* Must be a specially encoded meaning. */
{
switch (IntoidToSpecialCode (IntegerA))
{
case ISC_NEGATIVE_INFINITY:
Positive = FALSE;
case ISC_POSITIVE_INFINITY:
strcpy (ReversedNumber, GetIntoidsMessage (MSG_INTOIDS_INFINITY));
break;
case ISC_NOT_A_NUMBER:
default:
strcpy (ReversedNumber, GetIntoidsMessage (MSG_INTOIDS_NOT_A_NUMBER));
break;
}
strrev (ReversedNumber);
NextDigitPntr = ReversedNumber + strlen (ReversedNumber);
}
if (NextDigitPntr == ReversedNumber)
*NextDigitPntr++ = '0'; /* Empty string means zero. */
if (NextDigitPntr > EndOfReversedNumber)
{
strcpy (ReversedNumber,
GetIntoidsMessage (MSG_INTOIDS_NUMBER_TOO_BIG_TO_PRINT));
strrev (ReversedNumber);
NextDigitPntr = ReversedNumber + strlen (ReversedNumber);
}
/* Now have the number's digits. Find out if there is space for it. */
RequiredLength = (NextDigitPntr - ReversedNumber) + 1 /* For NUL */;
if (!Positive) RequiredLength++;
if (RequiredLength > BufferLength)
{
/* Number too big, fill buffer with '*' characters. */
while (BufferLength > 1)
{
*Buffer++ = '*';
--BufferLength;
}
}
else
{
/* Reverse copy the string to the output buffer. Put the minus sign in
front if it is negative. */
if (!Positive)
*Buffer++ = '-';
/* Copy the digits (or error message) over. */
while (--NextDigitPntr >= ReversedNumber)
*Buffer++ = *NextDigitPntr;
}
if (BufferLength > 0)
*Buffer = 0; /* End of string. */
return RequiredLength;
}
/****** intoids.library/AsciiToIntoid ***************************************
*
* NAME
* AsciiToIntoid -- Convert an ASCII string to an Intoid.
*
* SYNOPSIS
* New = AsciiToIntoid( Buffer, NextCharacterPntrPntr, Base, RecycleMe )
* D0 A0 A1 D0 D1
*
* Intoid AsciiToIntoid( STRPTR, char **, UWORD, Intoid );
*
* FUNCTION
* Converts a string to a number using the given base, much like
* the standard C function strtol. It recognizes numbers consisting of
* some leading space ((spaces or tabs) or international isspace() if
* locale.library is available), an optional sign, an optional base
* indicator if Base is zero, and finally a bunch of digits (which can
* also be the special current language specific strings for infinity or
* not-a-number).
*
* INPUTS
* Buffer - points to a buffer containing the NUL terminated string to
* be converted.
* NextCharacterPntrPntr - points to a user's pointer variable, or NULL
* if you don't want to use it. The user's pointer variable will be
* set to point to the character after the last one that was part of
* the number (or set to NULL if a bad error happened).
* Base - which number system to use. Base can be from 2 to 36. A Base
* of 0 can be used for automatic base determination (if the number
* starts with "0x" then it is treated as base 16, if it starts with
* "0" then base 8 is used, otherwise it defaults to base 10).
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* New - a newly allocated Intoid with the integer equivalent to the
* string in Buffer. NULL if out of memory or some other error
* happens (or if the string says not-a-number).
* NextCharacterPntrPntr - sets user's pointer to just after the number.
*
* NOTES
* The special strings for infinity and not-a-number are language
* dependent. If you read a file written in English on a French
* computer, it won't understand the special infinity values. Instead,
* you should use the portable binary formatting functions.
*
* SEE ALSO
* GetIntoidsMessage(), MSG_INTOIDS_NOT_A_NUMBER, MSG_INTOIDS_INFINITY.
*
*****************************************************************************
*/
Intoid LIBFUNC AsciiToIntoid (REGA0 STRPTR Buffer,
REGA1 char **NextCharacterPntrPntr, REGD0 UWORD Base,
REGD1 Intoid RecycleMe)
{
const char *CharPntr;
char Digit;
const char *DigitsStart;
char Letter;
int Log2OfBase;
unsigned long NewLength;
const char *NumberEnd;
const char *NumberStart;
BOOL IsPositive;
Intoid ReturnValue;
long TempLong;
if (NextCharacterPntrPntr != NULL)
*NextCharacterPntrPntr = NULL; /* In case of errors. */
if (Buffer == NULL || Base == 1 || Base > 36)
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_BAD_INPUT_PARMS));
return NULL; /* Can't do it. */
}
/* Skip leading space to get to the start of
the number (may be a sign or digit). */
NumberEnd = NULL; /* This only gets set when we have an answer. */
NumberStart = Buffer;
while (MyIsSpace ((unsigned long) *NumberStart))
++NumberStart;
/* Go past the sign to get to the digits. */
IsPositive = TRUE;
DigitsStart = NumberStart;
if (*DigitsStart == '-')
{
++DigitsStart;
IsPositive = FALSE;
}
else if (*DigitsStart == '+')
++DigitsStart;
/* Check for magic base indicators, only if Base is zero. */
if (Base == 0)
{
if (*DigitsStart == '0') /* Number starts with a leading zero. */
{
++DigitsStart; /* Skip leading zero. */
if (*DigitsStart == 'x' || *DigitsStart == 'X')
{
Base = 16;
++DigitsStart; /* Skip over the x. */
}
else
Base = 8;
}
else /* The default base. */
Base = 10;
}
/* Check for infinity or not a number strings. Use the utility.library
international string comparison functions. */
CharPntr = GetIntoidsMessage (MSG_INTOIDS_NOT_A_NUMBER);
if (Strnicmp ((STRPTR) CharPntr, (STRPTR) DigitsStart,
(long) strlen (CharPntr)) == 0)
{
NumberEnd = DigitsStart + strlen (CharPntr);
ReturnValue = NULL; /* Not a number. */
}
else
{
CharPntr = GetIntoidsMessage (MSG_INTOIDS_INFINITY);
if (Strnicmp ((STRPTR) CharPntr, (STRPTR) DigitsStart,
(long) strlen (CharPntr)) == 0)
{
NumberEnd = DigitsStart + strlen (CharPntr);
if (IsPositive)
ReturnValue = SpecialCodeToIntoid (ISC_POSITIVE_INFINITY);
else
ReturnValue = SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY);
}
}
if (NumberEnd == NULL)
{
/* Find number of bits in the base, each digit in the
input can have at most this many bits in the number. */
Log2OfBase = 0;
TempLong = Base;
while (TempLong != 0)
{
TempLong >>= 1;
Log2OfBase++;
}
/* Find the end of the number, and count digits. */
CharPntr = DigitsStart;
while ((Letter = *CharPntr) != 0)
{
if (Letter >= '0' && Letter <= '9') Digit = Letter - '0';
else if (Letter >= 'a' && Letter <= 'z') Digit = Letter - 'a' + 10;
else if (Letter >= 'A' && Letter <= 'Z') Digit = Letter - 'A' + 10;
else break;
if (Digit >= Base) break;
++CharPntr;
}
NumberEnd = CharPntr;
/* Now find the total number of bits that we might need. The actual
number may be smaller if there are leading zeroes. */
NewLength = (NumberEnd - DigitsStart) * (long) Log2OfBase;
/* If it doesn't fit in a small integer Intoid... */
if (NewLength > CHAR_PER_LONG * CHAR_BIT - 2)
{
/* May need a full IntRep, allocate one then reduce later. */
NewLength = NewLength / I_SHIFT + 1; /* Number of shorts we need. */
ReturnValue = ResizeIntoid (RecycleMe, NewLength, TRUE /* Zero it */);
RecycleMe = NULL; /* We have used it. */
/* Reading in a large number into an IntRep. Stop when an error
happens or if it already happened (out of memory). */
CharPntr = DigitsStart;
while ((Letter = *CharPntr) != 0 && ReturnValue != NULL)
{
if (Letter >= '0' && Letter <= '9') Digit = Letter - '0';
else if (Letter >= 'a' && Letter <= 'z') Digit = Letter - 'a' + 10;
else if (Letter >= 'A' && Letter <= 'Z') Digit = Letter - 'A' + 10;
else break;
if (Digit >= Base) break;
ReturnValue = MultiplyLongAndIntRep ((long) Base, ReturnValue,
ReturnValue);
ReturnValue = AddLongAndIntRep ((long) Digit, ReturnValue,
FALSE /* don't negate it */, ReturnValue);
++CharPntr;
}
if (IntoidIsIntRep (ReturnValue))
{
((IntoidAsPointer) ReturnValue)->positiveSign = IsPositive;
ReturnValue = NormalizeIntoid (ReturnValue);
}
else /* Something went wrong. Probably out of memory. */
{
FreeIntoid (ReturnValue);
ReturnValue = NULL;
}
}
else /* Number will fit in a small integer intoid. */
{
TempLong = 0;
CharPntr = DigitsStart;
while ((Letter = *CharPntr) != 0)
{
if (Letter >= '0' && Letter <= '9') Digit = Letter - '0';
else if (Letter >= 'a' && Letter <= 'z') Digit = Letter - 'a' + 10;
else if (Letter >= 'A' && Letter <= 'Z') Digit = Letter - 'A' + 10;
else break;
if (Digit >= Base) break;
TempLong = TempLong * Base + Digit;
++CharPntr;
}
if (!IsPositive)
TempLong = -TempLong;
ReturnValue = SmallIntToIntoid (TempLong);
}
}
FreeIntoid (RecycleMe); /* Frees it if it wasn't used up. */
if (NextCharacterPntrPntr != NULL)
*NextCharacterPntrPntr = (char *) NumberEnd;
return ReturnValue;
}
/****** intoids.library/CopyIntoid ******************************************
*
* NAME
* CopyIntoid -- Allocates a copy of an Intoid.
*
* SYNOPSIS
* NewIntoid = CopyIntoid( IntegerA, RecycleMe )
* D0 D0 D1
*
* Intoid CopyIntoid( Intoid, Intoid );
*
* FUNCTION
* Allocates memory (if needed) and fills it with a copy of IntegerA.
*
* INPUTS
* IntegerA - the Integer value you want to copy.
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* NewIntoid - A copy of IntegerA or NULL if out of memory.
*
*****************************************************************************
*/
Intoid LIBFUNC CopyIntoid (REGD0 Intoid IntegerA, REGD1 Intoid RecycleMe)
{
Intoid NewIntoid;
IntRepPointer NewRep;
if (IntoidIsIntRep (IntegerA))
{
NewRep = Icopy (IntoidIsIntRep (RecycleMe) ?
(IntoidAsPointer) RecycleMe : NULL,
(IntoidAsPointer) IntegerA);
if (NewRep != NULL)
Icheck (NewRep);
NewIntoid = NormalizeIntoid ((Intoid) NewRep);
}
else /* A small integer or special code. No auxiliary data. */
{
NewIntoid = IntegerA;
FreeIntoid (RecycleMe);
}
return NewIntoid;
}
/****** intoids.library/AddIntoids ******************************************
*
* NAME
* AddIntoids -- Adds two Intoids.
*
* SYNOPSIS
* NewIntoid = AddIntoids( IntegerA, IntegerB, RecycleMe )
* D0 D0 D1 A0
*
* Intoid AddIntoids( Intoid, Intoid, Intoid );
*
* FUNCTION
* Adds two intoids and returns a new one with the sum.
*
* INPUTS
* IntegerA - one value to be added.
* IntegerB - the other value to be added.
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* NewIntoid - newly allocated Intoid with a value of (A + B), or NULL
* if out of memory.
*
*****************************************************************************
*/
Intoid LIBFUNC AddIntoids (REGD0 Intoid IntegerA, REGD1 Intoid IntegerB,
REGA0 Intoid RecycleMe)
{
/* Most frequent case is two small integer Intoids. Just convert to longs
and add. They shouldn't overflow in the long addition, though the result
may be too big for a small integer (LongToIntoid will fix that). */
if (IntoidIsSmallInt (IntegerA) && IntoidIsSmallInt (IntegerB))
return LongToIntoid (IntoidToSmallInt (IntegerA) +
IntoidToSmallInt (IntegerB), RecycleMe);
/* If adding zero or not-a-number, return a copy of the other number. */
if (IntegerA == NULL || IntegerA == SmallIntToIntoid (0))
return CopyIntoid (IntegerB, RecycleMe);
if (IntegerB == NULL || IntegerB == SmallIntToIntoid (0))
return CopyIntoid (IntegerA, RecycleMe);
/* Check for adding infinity. The result is usually infinity. */
if (IntegerA == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY))
{
FreeIntoid (RecycleMe);
if (IntegerB == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
return SmallIntToIntoid (0); /* Well... */
return SpecialCodeToIntoid (ISC_POSITIVE_INFINITY);
}
if (IntegerB == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY))
{
FreeIntoid (RecycleMe);
if (IntegerA == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
return SmallIntToIntoid (0); /* Well... */
return SpecialCodeToIntoid (ISC_POSITIVE_INFINITY);
}
if (IntegerA == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
{
FreeIntoid (RecycleMe);
if (IntegerB == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY))
return SmallIntToIntoid (0); /* Well... */
return SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY);
}
if (IntegerB == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
{
FreeIntoid (RecycleMe);
if (IntegerA == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY))
return SmallIntToIntoid (0); /* Well... */
return SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY);
}
if (IntoidIsSpecialCode (IntegerA))
{
FreeIntoid (RecycleMe);
return IntegerA;
}
if (IntoidIsSpecialCode (IntegerB))
{
FreeIntoid (RecycleMe);
return IntegerB;
}
/* Check for numbers that fit in a long so we can do a simpler long/intrep
addition. Already know that not both are small integers. */
if (IntoidIsSmallInt (IntegerA))
return NormalizeIntoid (AddLongAndIntRep (IntoidToSmallInt (IntegerA),
IntegerB, FALSE, RecycleMe));
if (IntoidIsSmallInt (IntegerB))
return NormalizeIntoid (AddLongAndIntRep (IntoidToSmallInt (IntegerB),
IntegerA, FALSE, RecycleMe));
/* Ok, now adding two Intoids which are IntReps. */
return NormalizeIntoid (AddIntRepAndIntRep (IntegerA, FALSE,
IntegerB, FALSE, RecycleMe));
}
/****** intoids.library/NegateIntoid ****************************************
*
* NAME
* NegateIntoid -- Returns the negative of an intoid.
*
* SYNOPSIS
* NewIntoid = NegateIntoid( IntegerA, RecycleMe )
* D0 D0 D1
*
* Intoid NegateIntoid( Intoid, Intoid );
*
* FUNCTION
* Computes (0 - IntegerA).
*
* INPUTS
* IntegerA - the value you want to get the negative of.
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* NewIntoid - newly allocated Intoid containing (0 - IntegerA), or NULL
* if out of memory.
*
*****************************************************************************
*/
Intoid LIBFUNC NegateIntoid (REGD0 Intoid IntegerA, REGD1 Intoid RecycleMe)
{
Intoid Negated;
IntRepPointer NegatedRep;
Negated = CopyIntoid (IntegerA, RecycleMe);
if (IntoidIsSmallInt (Negated))
{
Negated = LongToIntoid (-IntoidToSmallInt (Negated), NULL);
}
else if (IntoidIsIntRep (Negated))
{
NegatedRep = (IntoidAsPointer) Negated;
NegatedRep->positiveSign = !NegatedRep->positiveSign;
/* Don't bother converting -0x40000000 to small integer form. */
}
else /* A special code. Only infinities change sign. */
{
if (Negated == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY))
Negated = SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY);
else if (Negated == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
Negated = SpecialCodeToIntoid (ISC_POSITIVE_INFINITY);
}
return Negated;
}
/****** intoids.library/AbsoluteIntoid **************************************
*
* NAME
* AbsoluteIntoid -- Computes absolute value of an Intoid.
*
* SYNOPSIS
* NewIntoid = AbsoluteIntoid( IntegerA, RecycleMe )
* D0 D0 D1
*
* Intoid AbsoluteIntoid( Intoid, Intoid );
*
* FUNCTION
* Computes the absolute value of an Intoid.
*
* INPUTS
* IntegerA - the value you want to get the negative of.
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* NewIntoid - newly allocated Intoid containing a copy of IntegerA if
* IntegerA was positive, or the negative of IntegerA if it was
* originally negative (the negative of a negative is positive), or
* NULL if out of memory.
*
*****************************************************************************
*/
Intoid LIBFUNC AbsoluteIntoid (REGD0 Intoid IntegerA,
REGD1 Intoid RecycleMe)
{
if (SignOfIntoid (IntegerA) < 0)
return NegateIntoid (IntegerA, RecycleMe);
return CopyIntoid (IntegerA, RecycleMe);
}
/****** intoids.library/SubtractIntoids *************************************
*
* NAME
* SubtractIntoids -- Computes value of one Intoid minus another.
*
* SYNOPSIS
* NewIntoid = SubtractIntoids( IntegerA, IntegerB, RecycleMe )
* D0 D0 D1 A0
*
* Intoid SubtractIntoids( Intoid, Intoid, Intoid );
*
* FUNCTION
* Does subtraction, IntegerA - IntegerB.
*
* INPUTS
* IntegerA - one input value.
* IntegerB - the other input value.
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* NewIntoid - newly allocated Intoid containing the value of
* (IntegerA - IntegerB), or NULL if out of memory.
*
*****************************************************************************
*/
Intoid LIBFUNC SubtractIntoids (REGD0 Intoid IntegerA, REGD1 Intoid IntegerB,
REGA0 Intoid RecycleMe)
{
/* Most frequent case is two small integer Intoids. Just convert to longs
and subtract. They shouldn't overflow in the long addition, though the
result may be too big for a small integer (LongToIntoid will fix that). */
if (IntoidIsSmallInt (IntegerA) && IntoidIsSmallInt (IntegerB))
return LongToIntoid (IntoidToSmallInt (IntegerA) -
IntoidToSmallInt (IntegerB), RecycleMe);
/* If subtracting zero or not-a-number, return a copy of the other number,
or a negative of it. */
if (IntegerA == NULL || IntegerA == SmallIntToIntoid (0))
return NegateIntoid (IntegerB, RecycleMe);
if (IntegerB == NULL || IntegerB == SmallIntToIntoid (0))
return CopyIntoid (IntegerA, RecycleMe);
/* Check for subtracting infinity. The result is usually infinity
except when subtracting the same infinity, which has a zero
result even though that probably isn't meaningful. */
if (IntegerA == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY))
{
FreeIntoid (RecycleMe);
if (IntegerB == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY))
return SmallIntToIntoid (0);
return SpecialCodeToIntoid (ISC_POSITIVE_INFINITY);
}
if (IntegerB == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY))
{
FreeIntoid (RecycleMe);
if (IntegerA == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY))
return SmallIntToIntoid (0);
return SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY);
}
if (IntegerA == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
{
FreeIntoid (RecycleMe);
if (IntegerB == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
return SmallIntToIntoid (0);
return SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY);
}
if (IntegerB == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
{
FreeIntoid (RecycleMe);
if (IntegerA == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
return SmallIntToIntoid (0);
return SpecialCodeToIntoid (ISC_POSITIVE_INFINITY);
}
if (IntoidIsSpecialCode (IntegerA))
{
FreeIntoid (RecycleMe);
return IntegerA;
}
if (IntoidIsSpecialCode (IntegerB))
{
FreeIntoid (RecycleMe);
return IntegerB;
}
/* Check for numbers that fit in a long so we can do a simpler long/intrep
addition. Already know that not both are small integers. */
if (IntoidIsSmallInt (IntegerA))
return NormalizeIntoid (AddLongAndIntRep (IntoidToSmallInt (IntegerA),
IntegerB, TRUE /* negate it */, RecycleMe));
if (IntoidIsSmallInt (IntegerB))
return NormalizeIntoid (AddLongAndIntRep (-IntoidToSmallInt (IntegerB),
IntegerA, FALSE, RecycleMe));
/* Ok, now adding two Intoids which are IntReps. */
return NormalizeIntoid (AddIntRepAndIntRep (IntegerA, FALSE,
IntegerB, TRUE /* negate it */, RecycleMe));
}
/****** intoids.library/CompareIntoids **************************************
*
* NAME
* CompareIntoids -- Signed comparison of two Intoid values.
*
* SYNOPSIS
* Result = CompareIntoids( IntegerA, IntegerB )
* D0 D0 D1
*
* LONG CompareIntoids( Intoid, Intoid );
*
* FUNCTION
* Compares the size of two Intoids, more efficiently than using a
* subtraction.
*
* INPUTS
* IntegerA - one input value.
* IntegerB - the other input value.
*
* RESULT
* Result - returns a long value that depends on the comparison:
* -1 for IntegerA < IntegerB,
* 0 for IntegerA == IntegerB,
* +1 for IntegerA > IntegerB.
*
* SEE ALSO
* SignOfIntoid(), CompareIntoidMagnitudes().
*
*****************************************************************************
*/
LONG LIBFUNC CompareIntoids (REGD0 Intoid IntegerA, REGD1 Intoid IntegerB)
{
long ComparisonResult;
Intoid IntegerResult;
unsigned short LengthA;
unsigned short LengthB;
IntRepPointer RepA;
IntRepPointer RepB;
BOOL SignA;
BOOL SignB;
/* Special case speed optimization for two IntRep based Intoids. */
if (IntoidIsIntRep (IntegerA) && IntoidIsIntRep (IntegerB))
{
RepA = (IntoidAsPointer) IntegerA;
RepB = (IntoidAsPointer) IntegerB;
LengthA = RepA->currentLength;
LengthB = RepB->currentLength;
SignA = RepA->positiveSign;
SignB = RepB->positiveSign;
ComparisonResult = SignA - SignB; /* Note that signs are 0=neg or 1=pos. */
if (ComparisonResult == 0) /* Have to do an effective subtraction. */
{
/* See which one is bigger, IntegerA or IntegerB. */
ComparisonResult = (long) LengthA - (long) LengthB;
if (ComparisonResult == 0) /* Same length, compare values. */
ComparisonResult = CompareNumberArrays (RepA->numberArray,
RepB->numberArray, LengthA);
if (ComparisonResult < 0) /* If B is bigger in magnitude. */
ComparisonResult = SignB ? -1L : 1L; /* Were doing A-B, so -B. */
else if (ComparisonResult > 0) /* If A is bigger in magnitude. */
ComparisonResult = SignA ? 1L : -1L;
/* Else equal and ComparisonResult is zero. */
}
}
else /* Small integers or special codes or a mixture of things. */
{
IntegerResult = SubtractIntoids (IntegerA, IntegerB, NULL);
ComparisonResult = IntoidToLong (IntegerResult);
FreeIntoid (IntegerResult);
if (ComparisonResult < 0)
ComparisonResult = -1L;
else if (ComparisonResult > 0)
ComparisonResult = 1L;
}
return ComparisonResult;
}
/****** intoids.library/CompareIntoidMagnitudes *****************************
*
* NAME
* CompareIntoidMagnitudes -- Comparison of absolute values.
*
* SYNOPSIS
* Result = CompareIntoidMagnitudes( IntegerA, IntegerB )
* D0 D0 D1
*
* LONG CompareIntoidMagnitudes( Intoid, Intoid );
*
* FUNCTION
* Compares the absolute values of two Intoids, more efficiently than
* using a subtraction of absolute values.
*
* INPUTS
* IntegerA - one input value.
* IntegerB - the other input value.
*
* RESULT
* Result - returns a long value that depends on the comparison:
* -1 for abs(IntegerA) < abs(IntegerB),
* 0 for abs(IntegerA) == abs(IntegerB),
* +1 for abs(IntegerA) > abs(IntegerB).
*
* SEE ALSO
* SignOfIntoid(), CompareIntoids().
*
*****************************************************************************
*/
LONG LIBFUNC CompareIntoidMagnitudes (REGD0 Intoid IntegerA,
REGD1 Intoid IntegerB)
{
long ComparisonResult;
Intoid IntegerResult;
unsigned short LengthA;
unsigned short LengthB;
IntRepPointer RepA;
IntRepPointer RepB;
Intoid TempA;
Intoid TempB;
/* Special case speed optimization for two IntRep based Intoids. */
if (IntoidIsIntRep (IntegerA) && IntoidIsIntRep (IntegerB))
{
RepA = (IntoidAsPointer) IntegerA;
RepB = (IntoidAsPointer) IntegerB;
LengthA = RepA->currentLength;
LengthB = RepB->currentLength;
/* See which one is bigger, IntegerA or IntegerB. */
ComparisonResult = (long) LengthA - (long) LengthB;
if (ComparisonResult == 0) /* Same length, compare values. */
ComparisonResult = CompareNumberArrays (RepA->numberArray,
RepB->numberArray, LengthA);
}
else /* Small integers or special codes or a mixture of things. */
{
TempA = AbsoluteIntoid (IntegerA, NULL);
TempB = AbsoluteIntoid (IntegerB, NULL);
IntegerResult = SubtractIntoids (TempA, TempB, NULL);
ComparisonResult = IntoidToLong (IntegerResult);
FreeIntoid (TempA);
FreeIntoid (TempB);
FreeIntoid (IntegerResult);
}
if (ComparisonResult < 0)
ComparisonResult = -1L;
else if (ComparisonResult > 0)
ComparisonResult = 1L;
return ComparisonResult;
}
/****** intoids.library/MultiplyIntoids *************************************
*
* NAME
* MultiplyIntoids -- Computes value of one Intoid times another.
*
* SYNOPSIS
* NewIntoid = MultiplyIntoids( IntegerA, IntegerB, RecycleMe )
* D0 D0 D1 A0
*
* Intoid MultiplyIntoids( Intoid, Intoid, Intoid );
*
* FUNCTION
* Does multiplication, IntegerA * IntegerB.
*
* INPUTS
* IntegerA - one input value.
* IntegerB - the other input value.
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* NewIntoid - newly allocated Intoid containing the value of
* (IntegerA * IntegerB), or NULL if out of memory.
*
*****************************************************************************
*/
Intoid LIBFUNC MultiplyIntoids (REGD0 Intoid IntegerA, REGD1 Intoid IntegerB,
REGA0 Intoid RecycleMe)
{
unsigned short BitsA;
unsigned short BitsB;
long LongA;
long LongB;
Intoid ResultIntoid;
/* If multiplying by zero or not-a-number, return zero. */
if (IntegerA == NULL || IntegerA == SmallIntToIntoid (0) ||
IntegerB == NULL || IntegerB == SmallIntToIntoid (0))
{
FreeIntoid (RecycleMe);
return SmallIntToIntoid (0);
}
/* If multiplying by positive 1, return the other argument. */
if (IntegerA == SmallIntToIntoid (1))
return CopyIntoid (IntegerB, RecycleMe);
if (IntegerB == SmallIntToIntoid (1))
return CopyIntoid (IntegerA, RecycleMe);
/* If multiplying by negative 1, return the negated other argument. */
if (IntegerA == SmallIntToIntoid (-1))
return NegateIntoid (IntegerB, RecycleMe);
if (IntegerB == SmallIntToIntoid (-1))
return NegateIntoid (IntegerA, RecycleMe);
/* Check for two small integer Intoids. Just convert to longs and do the
multiply. If it would overflow then make one of them an IntRep and do
the multiplication (have to do this after the zero and one tests since
MultiplyLongAndIntRep doesn't handle all those cases for the IntRep
argument). */
if (IntoidIsSmallInt (IntegerA) && IntoidIsSmallInt (IntegerB))
{
LongA = IntoidToSmallInt (IntegerA);
BitsA = 0;
if (LongA < 0) LongA = -LongA;
while (LongA != 0)
{
BitsA++;
LongA >>= 1;
}
LongB = IntoidToSmallInt (IntegerB);
BitsB = 0;
if (LongB < 0) LongB = -LongB;
while (LongB != 0)
{
BitsB++;
LongB >>= 1;
}
if (BitsA + BitsB <= CHAR_PER_LONG * CHAR_BIT - 2)
return LongToIntoid (IntoidToSmallInt (IntegerA) *
IntoidToSmallInt (IntegerB), RecycleMe);
/* Else result may be too big to fit in a long, so convert one argument to
an IntRep and do the long vs IntRep multiplication. */
RecycleMe = LongToIntRep (IntoidToSmallInt (IntegerB), RecycleMe);
if (RecycleMe == NULL)
ResultIntoid = NULL;
else
{
ResultIntoid = MultiplyLongAndIntRep (IntoidToSmallInt (IntegerA),
RecycleMe, RecycleMe);
ResultIntoid = NormalizeIntoid (ResultIntoid);
}
return ResultIntoid;
}
/* Check for multiplying by infinity. The result is infinity. */
if (IntegerA == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY) ||
IntegerA == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY) ||
IntegerB == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY) ||
IntegerB == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
{
LongA = IntoidToLong (IntegerA);
LongB = IntoidToLong (IntegerB);
FreeIntoid (RecycleMe);
if ((LongA < 0) == (LongB < 0))
return SpecialCodeToIntoid (ISC_POSITIVE_INFINITY);
else /* Have different signs. */
return SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY);
}
if (IntoidIsSpecialCode (IntegerA))
{
FreeIntoid (RecycleMe);
return IntegerA;
}
if (IntoidIsSpecialCode (IntegerB))
{
FreeIntoid (RecycleMe);
return IntegerB;
}
/* Check for numbers that fit in a long so we can do a simpler long/intrep
multiplication. Arguments can only be small integers or IntReps at this
point, and not both small integers. */
if (IntoidIsSmallInt (IntegerA))
return NormalizeIntoid (MultiplyLongAndIntRep (IntoidToSmallInt (IntegerA),
IntegerB, RecycleMe));
if (IntoidIsSmallInt (IntegerB))
return NormalizeIntoid (MultiplyLongAndIntRep (IntoidToSmallInt (IntegerB),
IntegerA, RecycleMe));
/* Have two IntReps, do the multiplication. */
return NormalizeIntoid (MultiplyIntRepAndIntRep (IntegerA, IntegerB,
RecycleMe));
}
/****** intoids.library/DivideIntoids ***************************************
*
* NAME
* DivideIntoids -- Computes value of one Intoid divided by another.
*
* SYNOPSIS
* NewIntoid = DivideIntoids( IntegerA, IntegerB, RecycleMe )
* D0 D0 D1 A0
*
* Intoid DivideIntoids( Intoid, Intoid, Intoid );
*
* FUNCTION
* Does division, IntegerA / IntegerB.
*
* INPUTS
* IntegerA - one input value.
* IntegerB - the other input value.
* RecycleMe - an old Intoid you want to deallocate, or NULL.
*
* RESULT
* NewIntoid - newly allocated Intoid containing the value of
* (IntegerA / IntegerB), or NULL if out of memory or if
* you divided by zero.
*
*****************************************************************************
*/
Intoid LIBFUNC DivideIntoids (REGD0 Intoid IntegerA, REGD1 Intoid IntegerB,
REGA0 Intoid RecycleMe)
{
long ComparisonResult;
long LongA;
long LongB;
/* Check for divide by zero or not-a-number. */
if (IntegerB == NULL || IntegerB == SmallIntToIntoid (0))
{
DisplayErrorMessage (GetIntoidsMessage (MSG_INTOIDS_DIVIDE_BY_ZERO));
FreeIntoid (RecycleMe);
return NULL;
}
/* Most frequent case is two small integer Intoids. Just
convert to longs and do it. Should never overflow. */
if (IntoidIsSmallInt (IntegerA) && IntoidIsSmallInt (IntegerB))
{
LongA = IntoidToSmallInt (IntegerA);
LongB = IntoidToSmallInt (IntegerB);
return LongToIntoid (LongA / LongB, RecycleMe);
}
/* If numerator is zero or NAN or just less than denominator
in magnitude, return zero. This also handles the case
where the divisor is infinity and the numerator is less. */
ComparisonResult = CompareIntoidMagnitudes (IntegerA, IntegerB);
if (IntegerA == NULL || IntegerA == SmallIntToIntoid (0) ||
ComparisonResult < 0)
{
FreeIntoid (RecycleMe);
return SmallIntToIntoid (0);
}
/* If the numerator and denominator are equal then return one or -1. */
if (ComparisonResult == 0)
{
ComparisonResult =
(SignOfIntoid (IntegerA) == SignOfIntoid (IntegerB)) ? 1L : -1L;
FreeIntoid (RecycleMe);
return SmallIntToIntoid (ComparisonResult);
}
/* If dividing by one, return the other value. */
if (IntegerB == SmallIntToIntoid (1))
return CopyIntoid (IntegerA, RecycleMe);
if (IntegerB == SmallIntToIntoid (-1))
return NegateIntoid (IntegerA, RecycleMe);
/* Check for numerator of infinity. The result is infinity. */
if (IntegerA == SpecialCodeToIntoid (ISC_POSITIVE_INFINITY) ||
IntegerA == SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY))
{
ComparisonResult =
(SignOfIntoid (IntegerA) == SignOfIntoid (IntegerB));
FreeIntoid (RecycleMe);
if (ComparisonResult)
return SpecialCodeToIntoid (ISC_POSITIVE_INFINITY);
else /* Have different signs. */
return SpecialCodeToIntoid (ISC_NEGATIVE_INFINITY);
}
/* Handle future special codes. Already taken care of not-a-number
and infinities before here. */
if (IntoidIsSpecialCode (IntegerA))
{
FreeIntoid (RecycleMe);
return IntegerA;
}
if (IntoidIsSpecialCode (IntegerB))
{
FreeIntoid (RecycleMe);
return IntegerB;
}
/* Check for numbers that fit in a long so we can do a simpler long/intrep
division. Note that IntRep denominator and long numerator won't occur
since that result would be zero if the IntRep is normalized. */
if (IntoidIsSmallInt (IntegerB))
return NormalizeIntoid (DivideIntRepByLong (IntegerA,
IntoidToSmallInt (IntegerB), RecycleMe));
/* Ok, have two IntReps. */
return NormalizeIntoid (DivideIntRepByIntRep (IntegerA, IntegerB,
RecycleMe));
}
/******************************************************************************
* Called by the SAS/C library wrapper when the library is expunged to clean
* up globally allocated things.
*/
void LIBFUNC __UserLibCleanup (REGA6 struct Library *LibraryBasePntr)
{
if (CurrentCatalog != NULL)
{
CloseCatalog (CurrentCatalog);
CurrentCatalog = NULL;
}
if (CurrentLocale != NULL)
{
CloseLocale (CurrentLocale);
CurrentLocale = NULL;
}
if (LocaleBase != NULL)
{
CloseLibrary (LocaleBase);
LocaleBase = NULL;
}
if (UtilityBase != NULL)
{
CloseLibrary (UtilityBase);
UtilityBase = NULL;
}
if (IntuitionBase != NULL)
{
CloseLibrary ((struct Library *) IntuitionBase);
IntuitionBase = NULL;
}
if (SysBase != NULL)
{
CloseLibrary ((struct Library *) SysBase);
SysBase = NULL; /* Do this last, CloseLibrary uses SysBase! */
}
}
/******************************************************************************
* Library initialisation code called by the SAS/C library wrapper. Open
* other libraries we need, do one time initialisation, returns zero if
* successful.
*/
long LIBFUNC __UserLibInit (REGA6 struct Library *LibraryBasePntr)
{
while (TRUE)
{
/* Open our copy of Execbase. Of course, OpenLibrary needs SysBase
defined before it is open, so just use the value from the magic
location 4 in memory for this one call. Needed for memory allocation
functions, opening libraries, etc. */
SysBase = (struct ExecBase *)*(struct Library **)4;
SysBase = (struct ExecBase *) OpenLibrary ((UBYTE *) "exec.library", 0);
if (SysBase == NULL)
break;
/* Open intuition.library (any version, so it works under AmigaDOS 1.3
too), just in case we need to display error messages. */
IntuitionBase = (struct IntuitionBase *)
OpenLibrary ((UBYTE *) "intuition.library", 0);
if (IntuitionBase == NULL)
break;
/* Need the utility library for international string comparison
functions and 32 bit multiply and divide subroutines. */
UtilityBase = OpenLibrary ((STRPTR) UTILITYNAME, 37);
if (UtilityBase == NULL)
{
DisplayErrorMessage ("Need " UTILITYNAME " version 37.");
break;
}
/* Open the locale library for international string support. It's
optional so a NULL LocaleBase won't stop the program. */
LocaleBase = OpenLibrary ((STRPTR) "locale.library", 0);
if (LocaleBase != NULL)
CurrentLocale = OpenLocale (NULL /* The default country & language */);
if (LocaleBase != NULL)
CurrentCatalog = OpenCatalog (CurrentLocale /* Can be NULL */,
(STRPTR) "Intoids.catalog", OC_BuiltInLanguage, "english", TAG_DONE);
return 0; /* Success! */
}
return 1; /* Failure. */
}